一、原型继承
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的nametwo.sayHi();
one中的属性和方法在two中可读取和使用
2、原型链
const one = {name: "jack",sayHi() {console.log("Hi")}}const two = {// name: 'xc'__proto__: one, // 原型继承,two继承one的属性};// 原型链,自下往上寻找内容// 原型继承不能形成闭环;__proto__:只能继承对象和nullconst three = {__proto__: two,}const four = {__proto__: three,}console.log(four.name); // jackfour.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(); // xcobj2.say(); // xlobj3.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); // trueconsole.log(Fn === fn.constructor); // trueconsole.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} , 没有原型