一、什么是原型链?
???function Fun1 () { ???????this.win = "skt" ???} ???Fun1.prototype.getVal = function () { ???????return this.win ???} ???function Fun2 () { ???????this.other_win = "rng" ???} ???Fun2.prototype = new Fun1 () ???Fun2.prototype.getOtherVal = function () { ???????return this.other_win ???} ???let instance = new Fun2() ???console.log(instance.getVal()) ?//skt
???搜索轨迹: instance1--> instance2 --> constructor2.prototype…-->Object.prototype
???let fun = function () {} ???console.log(fun.prototype) // object ???console.log(fun.__proto__) // function
???let obj = {} ???console.log(obj.prototype) // underfined ???console.log(obj.__proto__) // object
???function Fun() { ???????this.team = "rng" ???} ???let f = new Fun() ???console.log(f.team) // rng
上述代码中,我们通过new命令实例化了一个叫Fun的函数并赋值给f,这个新生成的实例对象f从构造函数Fun中得到了team属性,其实构造函数内部的this,就代表了新生成的实例对象,所以我们打印f.team的值就取到了rng这个值
这又是哪门子原理?答案如下?
- 创建一个空对象,作为将要返回的对象实例。
- 将这个空对象的原型,指向构造函数的
prototype
属性。 - 将这个空对象赋值给函数内部的
this
关键字。 - 开始执行构造函数内部的代码
也就是说,构造函数内部,this
指的是一个新生成的空对象,所有针对this
的操作,都会发生在这个空对象上。这也是为什么构造函数叫"构造函数"的原因,就是操作一个空对象(即this
对象),将其“构造”为所需要的样子。
如果我不加new呢?
???function Fun() { ???????this.team = "rng" ???} ???let f = Fun() ???console.log(f) // undefined ???console.log(team) // rng
我们可以看出上面打印f为undefined,而team却有值,这又是为什么?
其实在这种情况下,构造函数就变成了普通的函数,而且不会被实例.而此时的this指向了全局,team就变成了全局变量,因此我们取到了值
四、 __proto__指向哪?
说到__proto__的指向问题,还得取决于该对象创建时的实现方式.
辣么,到底有那些实现方式?
???let obj = {} ???console.log(obj.__proto__) ?// object ???console.log(obj.__proto__ === obj.constructor.prototype) // true 证明用字面量创建的函数,他的__proto__ 等于 该对象构造器的原型
2.构造器方式
???function Func () {} ???let a = new Func() ???console.log(a.__proto__) // object ???console.log(a.__proto__ === a.constructor.prototype) // true
???let obj1 = {name:"rng"} ???let obj2 = Object.create(obj1) ???console.log(obj2.__proto__) //{name: "rng"} ???console.log(obj2.__proto__ === obj2.constructor.prototype) // false
注: Object.create(prototype, descriptors) 创建一个具有指定原型且可选择性地包含指定属性的对象
五、如何确定原型和实例的关系?
???function Fun1 () { ???????this.laji = "uzi" ???} ???function Fun2 () { ???????this.strong = "faker" ???} ???Fun2.prototype = new Fun1() ???let fun2 = new Fun2 () ???console.log(fun2 instanceof Fun1) ???// true ???console.log(fun2 instanceof Fun2) ???// true ???console.log(fun2 instanceof Object) ?// true
由于原型链的关系,我们可以说fun2是一个对象Object,Fun1或是Fun2中任何一个类型的实例,所以这三个结果都返回了true
???console.log(Fun1.prototype.isPrototypeOf(fun2)) ?// true ???console.log(Fun2.prototype.isPrototypeOf(fun2)) ?// true ???console.log(Object.prototype.isPrototypeOf(fun2))// true
六、原型链的问题
什么?原型链还有问题?买了佛冷,why?
原因一: 当原型链中包含引用类型值的原型时,该引用类型值会被所有实例共享;
原因二:在创建子类型时,不能向超类型的构造函数中传递参数.
七、如何解决原型链问题?
???function Father () { ???????this.team = ["letme","mlxg"] ???} ???function Son () { ???????Father.call(this) ???} ???let son = new Son() ???son.team.push("uzi") ???console.log(son.team) ???????// ["letme", "mlxg", "uzi"] ???let little_son = new Son() ??????console.log(little_son.team) // ?["letme", "mlxg"]
组合继承, 有时候也叫做伪经典继承,指的是将原型链和借用构造函数的技术组合到一块,从而发挥两者优点的一种继承模式.
基本思想: 使用原型链实现对原型属性和方法的继承,通过借用构造函数来实现对实例属性的继承.
???function Father (team) { ???????this.team = team ???????this.people = ["mlxg","letme"] ???} ???Father.prototype.sayTeam = function () { ???????return console.log(this.team) ???} ???function Son (team,age) { ???????this.age = age ???????Father.call(this,team) ???} ???Son.prototype = new Father() ???Son.prototype.sayAge = function () { ???????return console.log(this.age) ???} ???let son = new Son("faker",8) ???son.people.push("uzi") ???console.log(son.people) ??// ["mlxg", "letme", "uzi"] ???son.sayAge() ?????????????//8 ???son.sayTeam() ????????????// faker ???let little_son = new Son("bang",3) ???console.log(little_son.people) ?// ["mlxg", "letme"] ????little_son.sayAge() ????????????// 3 ???little_son.sayTeam() ???????????// bang
我们可以看出,组合继承既保证了引用类型不再被所有实例所共享,也能够让子类型创建时向父类型传参,同时,原型中的方法又能够被复用,可以说是避免了原型链中的两大问题以及借用构造函数的缺陷,因此他也是js中最常用的继承方式,而且
???????function fun(o){ ???????????function F(){} ???????????F.prototype = o; ???????????return new F(); ???????}
???????let obj = {arr:[11,22] ???????fun(obj).arr.push(33) ???????console.log(fun(obj).arr) ?// [11,22,33]
???function fun(o){ ???????function F(){} ???????F.prototype = o; ???????return new F(); ???} ???let obj = {a:[11,22]} ???function createAnother(z) { ???????// 通过调用函数创建一个新对象 ???????var clone = fun(z); ???????clone.sayHi = function () { ???????????alert("hi"); ???????} ???????return clone; ???}
上面的例子中,我们把obj传入createAnother()函数中,返回的新对象clone不仅拥有了该属性,而且还被增强了,拥有了sayHi()方法;
等一下,这里要注意: 使用寄生式继承来为对象添加函数, 会由于不能做到函数复用而降低效率;这一点与构造函数模式类似.
5.寄生组合式继承
???function inheritPrototype(subType, superType) { ???????var protoType = Object.create(superType.prototype); ???//创建对象 ???????protoType.constructor = subType; ??????????????????????//增强对象 ???????subType.prototype = protoType; ????????????????????????//指定对象 ???} ???function Father(name) { ???????this.name = name; ???????this.colors = ["red", "blue", "green"]; ???} ???Father.prototype.sayName = function () { ???????console.log(this.name); ???} ???function Son(name, age) { ???????Father.call(this, name); ???????this.age = age; ???} ???inheritPrototype(Son, Father) ???Son.prototype.sayAge = function () { ???????console.log(this.age); ???} ???var instance = new Son("uzi", 3); ???instance.sayName(); //uzi ???instance.sayAge(); ?//3
inheritPrototype函数接收两个参数:子类型构造函数和超类型构造函数。
1. 创建超类型原型的副本。
2. 为创建的副本添加constructor属性,弥补因重写原型而失去的默认的constructor属性
3. 将新创建的对象(即副本)赋值给子类型的原型
inheritPrototype的高效率体现在它没有调用superClass构造函数,因此避免了在subClass.prototype上面创建不必要多余的属性. 同时,原型链还能保持不变,可以说是相当奈斯
js原型链
原文地址:https://www.cnblogs.com/qisi007/p/9844612.html