好了,我们看过了一些简单的用法,关于原型链的,下面我来说一些它常用到的模式
首先 我们来 看一下 in这个操作符,它有两个作用 单独使用和 for - in 使用,单独使用的作用是 in会给定对象的属性的返回值,无论是原型还是实例都返回true
function CreateCat() {
}
CreateCat.prototype.name = "小白";
CreateCat.prototype.age = 1;
CreateCat.prototype.color = "白色";
CreateCat.prototype.say = function () {
alert(this.name);
}
let cat1 = new CreateCat();
let cat2 = new CreateCat();
cat1.name = "小黑";
alert(cat1.name); // 来自于实例 =>小黑
alert(cat2.name); // 来自于原型 =>小白
alert(cat1.hasOwnProperty("name")); // true
alert(cat2.hasOwnProperty("name")); // false
alert("name" in cat1); // true
alert("name" in cat2); // true
下面我们来说说 Object.keys() 的用法, 可以枚举出对象上的实例
function CreateCat() {
}
CreateCat.prototype.name = "小白";
CreateCat.prototype.age = 1;
CreateCat.prototype.color = "白色";
CreateCat.prototype.say = function () {
alert(this.name);
}
let keys = Object.keys(CreateCat.prototype);
console.log(keys ); // 结果是 => ["name", "age", "color", "say"]
let cat1 = new CreateCat();
cat1.name = "小黑";
cat1.age=5;
let keyscat1 = Object.keys(cat1); // 结果是 => ["name", "age"]
console.log(keyscat1);
在上面 我们看到的例子中 每次都要敲 CreateCat.prototype 这个样子很麻烦,那么我们的简单方法是什么呢,原型字面量语法
function CreateCat() {
}
CreateCat.prototype = {
name:"小白",
age:1,
color:"白色",
say: function () {
alert(this.name);
}
};
// 但是这个样子有一个问题,因为我们的每创建一个函数,就会同时创建它的prototype对象和constructor对象,但是我们使用 字面量原型语法,那么constructor不在指向CreateCat 这个对象 ,而是指向了 Object
let cat = new CreateCat();
alert(cat instanceof Object); // true
alert(cat instanceof CreateCat); // true
alert(cat.constructor == Object); // true
alert(cat.constructor == CreateCat);// false
// 如果你要用到的 constructor 很重要 可以通过这样子来实现
function CreateCat() {
}
CreateCat.prototype = {
constructor: CreateCat, // 把CreateCat 赋值给constructor
name:"小白",
age:1,
color:"白色",
say: function () {
alert(this.name);
}
};
let cat = new CreateCat();
alert(cat instanceof Object); // true
alert(cat instanceof CreateCat); // true
alert(cat.constructor == Object); // false 不在是Object 了
alert(cat.constructor == CreateCat);// true 我们可以看到已经指向了CreateCat
我们来看一下 如果在最开始 我们添加最原始的执行,那么它将切断原型链之间的关系
function CreateCat() {
}
// 当构造函数指向最初的原型prototype指针,那么它将切断原型链之间的关系
let cat = new CreateCat();
CreateCat.prototype = {
constructor: CreateCat, // 把CreateCat 赋值给constructor
name:"小白",
age:1,
color:"白色",
say: function () {
alert(this.name);
}
};
cat.say(); // 结果是 => cat.say is not a function
在原型中很多实例是被共享的,那么一个引用类型,会发生什么事情呢,请看例子
function CreateCat() {
}
CreateCat.prototype = {
constructor: CreateCat, // 把CreateCat 赋值给constructor
name:"小白",
age:1,
color:"白色",
friends: ["小红", "小绿"],
say: function () {
alert(this.name);
}
};
let cat1 = new CreateCat();
let cat2 = new CreateCat();
// 给cat1 实例上添加一个朋友
cat1.friends.push("小黑");
console.log(cat1.friends); //结果是 => ["小红", "小绿", "小黑"]
// 什么 cat2上面的实例 也被影响了,因为原型链上是共享的,所以在引用类型上,是一样的,这明显不是我们想要的
console.log(cat2.friends); //结果是 => ["小红", "小绿", "小黑"]
console.log(cat1.friends == cat2.friends) // true
那么我们的解决方案是什么呢
我们使用 构造模式 和 原型模式,把要影响的放入到 构造函数中,通用的方法,放入到原型对象中,请看下面的例子
function CreateCat(name, age, color) {
this.name = name;
this.age= age;
this.color = color;
this.friends = ["小红", "小绿"];
}
CreateCat.prototype = {
constructor: CreateCat, // 把CreateCat 赋值给constructor
say: function () {
alert(this.name);
}
};
let cat1 = new CreateCat("小黑", 2, "黑色");
let cat2 = new CreateCat("小白", 1, "白色");
// 现在我们往cat1的 实例中添加小黄
cat1.friends.push("小黄");
console.log(cat1.friends); // ["小红", "小绿", "小黄"]
console.log(cat2.friends); // ["小红", "小绿"]
console.log(cat1.friends === cat2.friends); // false
console.log(cat1.say === cat2.say); // true
寄生构造函数模式
function CreateCat(name, age, color) {
let cat = new Object();
cat.name = name;
cat.age= age;
cat.color = color;
cat.say = function () {
console.log(this.name);
}
return cat;
}
let cat1 = new CreateCat("小白", 2, "白色");
cat1.say(); // 结果是 => 小白
console.log(cat1 instanceof CreateCat); // false 与原型和实例都没有关系
console.log(cat1 instanceof Object); // true
// 这中模式 返回的对象 和 构造函数的原型属性没有任何 关系,所以有其他模式的话,不要使用这种模式
如果想更深一步的了解,请参考博客
面向对象和原型链的用法(上)
面向对象和原型链的用法(下)
好了,有什么问题欢迎指出哦,一起进步,哈哈哈