一、原型继承
1、[ [ Prototype ] ],对象属性
所有对象都有一个[ [ Prototype ] ]
当从object
中读取一个缺失的属性时,JavaScript 会自动从原型中获取该属性,“原型继承”。
其中之一设置原型的方法,使用特殊的名字 __proto__
:
const one = {
name: "jack",
sayHi() {
console.log("Hi")
}
}
const two = {
// name: 'xc'
};
two.__proto__ = one; // 原型继承,two继承one的属性
console.log(two.name); // one的name
two.sayHi();
one中的属性和方法在two中可读取和使用
2、原型链
const one = {
name: "jack",
sayHi() {
console.log("Hi")
}
}
const two = {
// name: 'xc'
__proto__: one, // 原型继承,two继承one的属性
};
// 原型链,自下往上寻找内容
// 原型继承不能形成闭环;__proto__:只能继承对象和null
const three = {
__proto__: two,
}
const four = {
__proto__: three,
}
console.log(four.name); // jack
four.sayHi(); // Hi
four中没有,会再从three中招,然后是two,one,找到为止
但是:1.继承不能产生闭环;2.继承的只能是对象和null
3、写入不使用原型 ,修改属性不会对原型产生任何影响
原型仅用于读取属性。对于写入/删除操作可以直接在对象上进行。
const one = {
name: "jack",
};
const two = {
__proto__: one,
};
const three = {
__proto__: two,
};
three.name = "xxx";
// one中的“name”没受到影响
console.log(three); // {name: 'xxx'}
console.log(one); // {name: 'jack', sayHi: ƒ}
访问器(get / set)是例外 ,设置与访问器有关的属性时,会调用原型中的访问器
const one = {
firstName: "xu",
lastName: "chuang",
get fullName() {
return `${this.firstName} ${this.lastName}`;
},
set fullName(value) {
[this.firstName, this.lastName] = value.split(" ");
},
};
const two = {
firstName: "zhang",
lastName: "li",
__proto__: one,
};
console.log(two.fullName); // zhang li
// 第一次查找:在two中未找到fullName,在one中找到fullName方法;
// 第二次查找:在two中找到firstName和lastName,执行该方法
two.fullName = "xu li"; // 原型one中的set被调用了
console.log(two); // {firstName: 'xu', lastName: 'li'}
4、原型继承时this的值
无论在哪里找到方法:在一个对象还是在原型中。在一个方法调用中,this
始终是点符号 .
前面的对象。
const obj1 = {
name: "xc",
say() {
console.log(this.name)
}
}
const obj2 = {
name: "xl",
__proto__: obj1,
}
const obj3 = {
name: "xyp",
__proto__: obj2,
}
// 无论在哪里找到方法:在一个对象还是在原型中。在一个方法调用中,this 始终是点符号 . 前面的对象。
obj1.say(); // xc
obj2.say(); // xl
obj3.say(); // xyp
5、for...in 循环会迭代继承的属性
想排除继承的属性:
obj.hasOwnProperty(key):如果 obj
具有自己的(非继承的)名为 key
的属性,则返回 true
。
const obj1 = {
name: 'xc',
age: 20,
gender: "male"
};
const obj2 = {
name: "xl",
__proto__: obj1,
};
for (let i in obj2) {
if (obj2.hasOwnProperty(i)) console.log(obj2[i]);
// 没有判断:xl 20 male ; 有if判断:xl
}
二、F.prototype
1、构造函数创造对象时设置原型
new F()
构造函数创建一个新对象。如果 F.prototype
是一个对象,那么 new
操作符会使用它为新对象设置 [[Prototype]]
。
F.prototype
指的是 F
的一个名为 "prototype"
的常规属性。这听起来与“原型”这个术语很类似,但这里实际上指的是具有该名字的常规属性。可以任意设置F的“prototype”。
// 构造函数(实例)对象指向构造函数的原型
function Fn() {
// this={}
// ! this.__proto__=Person.prototype
// return this
};
const fn = new Fn()
console.dir(Fn);
console.log(fn);
console.dir(Fn.prototype == fn.__proto__)
2、默认的F.prototype
每个函数都有 "prototype"
属性。默认的 "prototype"
是一个只有属性 constructor
的对象,属性 constructor
指向函数自身。constructor
属性可以通过 [[Prototype]]
给所有 构造函数对象 使用。
function Fn() {
// this={}
// ! this.__proto__=Person.prototype
// return this
};
const fn = new Fn()
console.dir(Fn);
console.log(fn);
console.log(Fn === Fn.prototype.constructor); // true
console.log(Fn === fn.constructor); // true
console.log(Fn.prototype.constructor === fn.constructor); // true
三、原生的原型
1、Object.prototype
所有对象的原型链的终点:Object;
Object的原型是null。
// 原生的原型
/* function Object() {
this = {}
this.__proto__ = Object.prototype
return this
} */
// const obj1= new Object()
const obj1 = {};
console.log(obj1.__proto__ === Object.prototype);
console.log(Object.prototype.__proto__ ); // null 原生
2、其他内建原型
其他内建对象,像 Array
、Date
、Function
及其他,在prototype中都自带了各种方法。
当我们创建一个数组 [1, 2, 3]
,在内部会默认使用 new Array()
构造器。 Array.prototype
变成了这个数组的 prototype,并为这个数组提供数组的操作方法。
3、更改原生原型(不推荐使用)
// 原生原型的方法所有都能用
Array.prototype.say = function () {
console.log("hello");
}
const arr = [];
arr.say();
四、原型方法,没有__proto__的对象
1、原型方法
Object.create(proto,{??}) 创造一个对象,以proto为原型,以及可选的属性描述来创建一个空对象;
Object.getPrototypeof(obj)返回对象obj的[ [ Prototype ] ];
Object.setPrototypeof(obj,proto)设置对象obj的原型为proto;
Object.create
方法更强大,因为它有一个可选的第二参数:属性描述器。
我们可以在此处为新对象提供额外的属性,就像这样:
const Person = { name: "jack" };
const obj = Object.create(Person, {
age: {
value: 20
},
gender: {
value: "boy"
}
});
console.log(obj);
实现对对象Person的完全复制:
let clone = Object.create(
Object.getPrototypeOf(Person),
Object.getOwnPropertyDescriptors(Person),
)
2、没有__proto__的对象
// 极简对象“Very plain” object,原型为null
const obj = { a: 1, b: 2 }
Object.setPrototypeOf(obj, null);
console.log(obj); // {a: 1, b: 2} , 没有原型