js 中的原型prototype

每次创建新函数,就会根据规则为该函数创建一个 prototype 属性,该属性是一个指向函数原型对象的指针。并且原型对象都默认拥有一个 constructor 属性,该属性是一个指向那个新建函数的指针。

1、常规原型对象

function Person(){
}
Person.prototype.name = "Tom";
Person.prototype.job = "software engineer";
Person.prototype.age = 29;
Person.prototype.sayName = function (){
    console.log(this.name);
};

var p1 = new Person();
var p2 = new Person();

p1.sayName();  // Tom
p2.sayName();  // Tom

console.log(p1.sayName == p2.sayName);  // true

下图展示了构造函数、原型对象、对象实例的关系

 关于[[prototype]],在创建一个实例后,该实例内部将包含一个指向原型对象的指针[[prototype]],这个属性在有的实现中可以通过对象的 __proto__属性访问(有的浏览器或者执行环境不能)。要注意的是该属性是实例与构造函数的原型对象的联系,而不是实例与构造函数之间的联系。

上述两个对象实例 p1 和 p2 自身是没有属性和方法的,但都能调用sayName()方法。这是因为当读取某个属性时,会执行一次搜索,先从对象实例自身开始,如果本身有给定属性,则返回该属性的值;如果没有找到,则继续从它的原型对象中查找,找到则返回,没找到继续从原型对象的原型对象(如果该实例的原型对象有原型对象的话)中查找。找到为止,直至原型链的末端,如果最后还是没找到则报错。

介绍一些方法

function Person(){
}
Person.prototype.name = "Tom";
Person.prototype.job = "software engineer";
Person.prototype.age = 29;
Person.prototype.sayName = function (){
    console.log(this.name);
};

var p1 = new Person();
var p2 = new Person();

p1.sayName();  // Tom
p2.sayName();  // Tom

console.log(p1.sayName == p2.sayName);  // true

//判断对象之间是否是原型对象--实例的关系
console.log(Person.prototype.isPrototypeOf(p1));  //true
console.log(Person.prototype.isPrototypeOf(p2));  //true

//Object.getPrototypeOf(),返回实例对象的[[prototype]]的值,即原型对象
console.log(Object.getPrototypeOf(p1) == Person.prototype); // true

//hasOwnProperty(),继承自Object,检测对象自身是否存在某个属性
console.log(p1.hasOwnProperty("name")); //false

// in 操作符,只要能通过对象访问到属性就返回true
console.log("name" in p1); // true

//for-in 遍历所有能够访问到的且可枚举的属性
for(var prop in p1){
    console.log(prop); //name,job,age,sayName
}

// Object.keys(),返回参数对象自身的所有可枚举属性的字符串数组
var keys = Object.keys(Person.prototype);
console.log(keys); //["name","job","age","sayName"]

//Object.getOwnPropertyNames(),返回参数对象自身的所有属性的字符串数组,包含不可枚举属性
keys = Object.getOwnPropertyNames(Person.prototype);
console.log(keys); //["constructor","name","job","age","sayName"]

p1.name = "Greg";

console.log(p1.name); // Greg --来自实例
console.log(p2.name); // Tom  --来自原型

console.log(p1.hasOwnProperty("name")); //true
console.log(p2.hasOwnProperty("name")); //false

console.log("name" in p1); // true
console.log("name" in p2); // true

console.log(Object.keys(p1));  //["name"]
keys = Object.getOwnPropertyNames(p1);
console.log(keys); ////["name"]

delete p1.name;
console.log(p1.name);  //Tom   --来自原型
console.log(p1.hasOwnProperty("name")); //false

 

2、对象字面量形式的原型

function Person(){
}

Person.prototype = {
    name : "Tom",
    job : "software engineer",
    age : 29,
    sayName : function (){
        console.log(this.name);
    }
};

var p1 = new Person();
var p2 = new Person();

p1.sayName();  // Tom
p2.sayName();  // Tom

console.log(p1.sayName == p2.sayName);  // true
console.log(p1 instanceof Object);  //true
console.log(p1 instanceof Person);  //true
console.log(p1.constructor == Person);  //false
console.log(p1.constructor == Object);  //true

对象字面量形式的原型,实际上是完全重写了默认的 prototype 对象,因此 constructor 属性已经是新对象的constructor属性了,指向Object,不再指向Person函数了。

可以在原型重写时加上constructor属性,手动添加属性,会导致它的[[enumerable]]特性设置为true,即设为可枚举的,而默认的原生constructor 属性是不可枚举的。

function Person(){
}

Person.prototype = {
    //constructor: Person,
    name : "Tom",
    job : "software engineer",
    age : 29,
    sayName : function (){
        console.log(this.name);
    }
};
//可以特别定义为不可枚举
Object.defineProperty(Person.prototype,"constructor",{
    enmuerable:false,
    value:Person
});

字面量对象重写原型可能会出现一些错误:

function Person(){
}

var p1 = new Person();

Person.prototype = {
    constructor: Person,
    name : "Tom",
    job : "software engineer",
    age : 29,
    sayName : function (){
        console.log(this.name);
    }
};

var p2 = new Person();

p2.sayName();  // Tom
p1.sayName();  // error : p1.sayName is not a function

上面例子中,先创建 p1 对象,后重写原型,导致 p1 的原型对象不是新的原型对象,所以执行 p1.sayName() 时,从 p1 的原型中找不到 sayName 方法。

如果支持__proto__属性的话,可以在重写原型对象后,添加语句:p1.__proto__ = Person.prototype; 重新建立实例和原型的关联。

猜你喜欢

转载自www.cnblogs.com/zhanglw456/p/10407566.html