在ES6的Class到来之前,先总结下个人对js中prototype属性的理解。
1、构造函数(大写函数名 this 无return) 2、原型对象(函数.prototype) 3、实例对象( new出来的东西obj )
备注:本文举例子都以Fn作为构造函数 Fn.prototype作为原型对象 实例对象obj = new Fn()
构造函数中的this是指向window的,只有用了new方法来调用,它才成为了构造函数,new调用时候发生了this指向的改变,指向了实例对象。
为什么会引出prototype这个东东(这个属性是一个指针,指向一个对象)??
因为:1、每次new实例的时候,都会创建内部的所有属性和方法 --- 很浪费
2、每次new出来的实例对象 他们并不相等 --- 比较的是指针,指针并不相等
所以:把其公用的(什么叫公用的,就是每个new出对象都需要的)属性和方法写在原型对象prototype上,(构造函数和原型对象由指针指向关系)
这样即可保证只有一次的创建(节省性能) 还可以保证 实例对象 的指针相同,也就是他们相同
谁有prototype这个属性? 每个函数都有 通过Function.prototype.bind方法构造出来的函数是个例外,它没有prototype属性
谁有__proto__属性? 每个对象都有(函数也是对象,所以函数也有此属性) 一个对象的隐式原型 指向 构造该对象的构造函数的原型
函数的__proto__ 属性例子: function F(){};console.log(F.__proto__ === Function.prototype)
谁有constructor这个属性? 每个对象都有,因为此属性是Obejct有的,在其他对象的原型链上,继承过来的
所以说构造函数原型属性prototype上两个很独特的自带属性就是constructor和__proto__。
2、内建对象的构造器函数
3、原型的动态性:
动态性和new创建的时机无关,不管修改prototype和new谁先谁后(前提是不要重写):
我们对 原型对象prototype 所做的任何修改都能够立即从 实例obj 上反映出来—— 即使是先创建了实例后修改(而不是后创建)原型也照样如此,后创建叫做重写原型对象
实例与原型之间的连接只不过是一个指针,松散连接。 请记住:实例中的指针仅指向原型,而不指向构造函数。
调用构造函数时会为实例添加一个指向最初原型的__Prototype__ 指针,而把原型修改为另外一个对象就等于切断了构造函数与最初原型之间的联系。。
可以看出早以前创建的实例friend还依旧指向最初的person.prototype,在后面重写了person.prototype,而friend指针却没变,依旧指向了旧的。
正所谓 一颗赤心向明月,奈何过往已惘然,独守空城遥遥望,不知去日不可覆。
说明了:修改属性可以获得动态改变,但一旦重写了,新功能已然与你无关,不在同一世界了,往事已切断
如果此时在new新的对象,那么新的对象的__proto__是指向新的,和旧的对象指向不是同一个prototype。那么也可以看出,动态性和重写是两个不相关的东西,不能混为一谈
注意Fn.prototype添加(归于修改里面)和重写带来的影响
添加:Fn.prototype.aaaa = function XXX(){} 添加:这个是挂接,挂接前后 obj.__proto__ === Fn.prototype === obj.constructor.prototype
重写:Fn.prototype = { aaaa : function XXX(){}} 重写:这个是赋值 赋值以后 obj.__proto__ === Fn.prototype !== obj.constructor.prototype,
因为此时Fn.prototype的constructor属性指向了Object。
最常规的写法(有个致命的问题):
function Person(){}
Person.prototype = {
name : "Nicholas",
age : 29,
job: "Software Engineer",
sayName : function () {alert(this.name);}
};
var boy = new Person();
结论: boy instanceof Person // true boy instanceof Object // true boy.constructor === Object // true boy.constructor === Person // false
说明了 Person.prototype = {key : value}这种事粗暴的重写,破坏了__Prototype__ 指针,需要补救
方法1、在最后补上 Person = Person.prototype.constructor
方法2、在对象内部填上constructor属性,以这种方式重设 constructor 属性会导致它的 [[Enumerable]] 特性被设置为 true。
Person.prototype = {
constructor : Person,
name : "Nicholas",
age : 29,
job: "Software Engineer",
sayName : function () {alert(this.name);}
};
方法3: Object.defineProperty()
Object.defineProperty(Person.prototype, "constructor", {
enumerable: false,
value: Person
});
最正确的但是比较繁琐的是给Person.prototype添加属性和方法(修改)
如:Person.prototype.name = "Nicholas"
.........
Person.prototype.sayName = function () {alert(this.name);}
属性查找和删除属性
找属性的时候,先找本身的,有的话就使用,没有的话层层上找。对象实例添加一个属性时,这个属性就会屏蔽原型对象中保存的同名属性,记住是屏蔽而不是修改
删除本身属性 可用delete方法来删除 得到的都是原型链继承来的属性 ,delete并不能删除原型链上的属性和方法,只能删除对象自己挂接的
4、钩子,原型链(也称__proto__链)
__proto__钩子指向谁(看此对象的创建方式,分三种)???
原型链:
js中万物皆对象,__proto__是所有对象都有的属性,所以可以从最开始的obj通过__proto__钩子连接,直到最后的null
读取对象属性/方法沿着原型链的走势:会先看当前对象中是否有这个属性,如果没有的话,就会查找它的prototype对象是否有这个属性,如此递推下去,一致检索到Object内建对象。