JS 原型相关理解
1. 理解原型对象:每个函数都有 prototype 属性,这个属性是一个指针,指向原型对象。所有原型对象都有一个 constructor 属性,这个属性也是一个指针,指向构造函数。所有的对象(包括实例和原型)中,都有 __proto__ 属性,用于连接实例和构造函数的原型,而不是连接实例和构造函数。确定实例和原型对象的关系,用下面两个方法,返回的是布尔值
Person.prototype.isPrototypeOf(p1);
Object.getPrototypeOf(p1) == Person.prototype;
若在实例 1 中创建和原型对象中相同名字的属性,那么对于实例 1 来说,会覆盖掉原型中的同名属性。用下面的方法,可以判断,该属性是势力中的还是原型对象中的。该方法返回布尔值。
function Person(name, age) {
this.name = name;
this.age = age;
this.friends = ['a', 'b']
}
Person.prototype = {
sayhi : function() {
console.log("hi,", this.name)
}
}
var p1 = new Person("Jhon", "23")
p1.hasOwnProperty("name");
2. 原型与 in 操作符:in 操作符用于判断,实例中是否有该属性或方法,无论这个属性和方法是在原型对象中,还是在实例中,都会返回 true。
"name" in p1;
3. 更简单的原型语法:之前的例子中,为原型对象添加属性和方法时,要用 Person.prototype,为了在视觉上更好的封装原型功能,更常见的做法是用对象字面量来重写整个原型对象。但是要注意的是,用对象字面量的形式重写原型对象,原型对象的 constructor 属性的指向,不再是构造函数了,而是 object。原因是,重写的对象字面量也是一个新的 object 实例。但此时,instanceof 还是可以指向构造函数,但是 验证 constructor 却不再是构造函数了。
function Person(name, age) { this.name = name; this.age = age; this.friends = ['a', 'b'] } Person.prototype = { obj: "doctor", sayHi: function(){ console.log("hello", this.name); } }
var friend = new Person("Jhon", "23")
console.log(friend instanceof Object); // true
console.log(friend instanceof Person); // true
console.log(friend.constructor == Person); // false
console.log(friend.constructor == Object); // true
修改办法,在 Person.prototype 里面加上一句: constructor:Person
function Person(name, age) { this.name = name; this.age = age; this.friends = ['a', 'b'] } Person.prototype = {
constructor: Person, obj: "doctor", sayHi: function(){ console.log("hello", this.name); } }
4. 原型的动态性:先创造实例,再为原型对象添加方法和属性,那么实例也可以访问到原型对象中新添加的属性和实例。这是因为实例和原型之间的松散连接关系,当我们调用 frined.sayHi() 时,先在实例中寻找,找不到再去原型对象中寻找。但要注意,若在实例创建之后,重新编写了整个原型对象,那么,之前创建的实例就不能继承现在重新编写的原型对象的属性和方法了。看例子:
先创建实例,再为原型对象添加方法:
function Person(name, age) { this.name = name; this.age = age; this.friends = ['a', 'b'] } var p1 = new Person("Jack", "23"); Person.prototype.sayHi = function() {
console.log("hi,", this.name);
}
p1.sayHi(); // hi, Jack
先创建实例,再用对象字面量的方式重新编写原型对象
function Person(name, age) { this.name = name; this.age = age; this.friends = ['a', 'b'] } var p1 = new Person("Jack", "23"); Person.prototype = { sayhi : function() { console.log("hi,", this.name) } } p1.sayHi(); // error
尽管能随时为原型对象添加属性和方法,但是重写编写的情况就不一定了。原因是,调用构造函数时,会为实例添加一个指向最初原型的 __proto__ 指针,而把原型改写另一个对象,就等于切断了构造函数和最初原型对象的联系。而实例中的 __proto__ 指针指向原型对象,不是指向构造函数,所以,之前创建的实例,访问不到重新编写原型对象中的属性和方法了。