对于前端初学者来说,JS 原型链一直是个比较难理解的点。前段时间,在学习原型链时,便写了例子。经过加工就有了这篇文章。为了让大家看明白,我把例子拆开一步步来看,相信多看几遍就懂了。
注意:一定要看注释!看注释!看注释!, 文章末尾有完整的原型链关系图和完整的demo
文章是前段时间写的,有些不足和错误的地方,正在修改中..
几个重要的概念
new 一个实例对象 (单身狗表示很好使)
为了弄清楚下面几个概念,我们先 new 个对象来说明一下
// person() 构造函数
function person(name, age) {
this.name = name;
this.age = age;
this.country = "China";
}
// es6 写法
// class person {
// constructor(name, age) {
// this.name = name;
// this.age = age;
// this.country = "China";
// }
// }
// kite 实例对象
const kite = new person("Kite", 23);
这个几个概念,不理解也没有关系,脑子里有这个概念,后面看了自然就懂了。
实例对象
: 首先你要有一个函数(方法), 用new
关键字来创建一个对象, 而这个对象,就叫实例对象
。例:const kite = new person("Kite", 23)
。这里的kite
就是实例对象构造函数
: 其实就是 js 普通的函数,之所以叫构造函数
,是相对实例对象
来说的。主要用来初始化对象,即为对象成员变量赋初始值,它总与 new 一起使用。我们可以把对象中一些公共的属性和方法抽取出来,然后封装到这个函数里面,例:person()
prototype
属性: 只有函数才有prototype
属性,指向函数的原型对象
,例:person.prototype
__proto__
属性:函数和对象都有__proto__
属性,指向这个对象的构造函数的prototype
属性,也就是构造函数的原型。例:kite.__proto__
对象属性访问(调用)原理
默认情况下,对象的调用属性时,会先检查自己有没有这个属性,如果有则用自己的,如果没有,就会去构造函数中找,一层一层得往上找(本质就是沿着__proto__
这对链找),直到找到为止,如果都没有,则返回undefind
例如:
// kite 对象本身的属性
console.log(kite.name); //Kite
// kite 对象本身的属性
console.log(kite.age); // 23
// kite 对象本身的属性
console.log(kite.country); //China
// 在原型对象中新增 color 属性
person.prototype.color = "yellow";
// person 原型对象上的属性,在 Chrome 控制台中 color属性是放在 [[Prototype]]中的,用来区分是不是原型对象上的属性
console.log(kite.color); // yellow
//既没在实例对象 kite 中找到,也没有在 kite 的原型对象上找到,所以返回 undefined
console.log(kite.a); // undefinde
constructor 属性
// constructor属性指向该对象的构造函数(person)
console.log(kite.constructor === person);
proto 、 prototype 、 constructor 属性的关系
// 注意:对象只有 __proto__ 属性,方法(js中方法其实也是对象)既有 __proto__ 属性,也有 prototype 属性
console.log(kite.__proto__ === person.prototype); //true, 说明对象的`__proto__`属性,指向这个对象的构造函数的`prototype`属性,也就是原型对象
// 说明 name、age、country 是在实例对象上的,而不是在原型对象上的
console.log(kite.name === kite.__proto__.name); //false
console.log(kite.age === kite.__proto__.age); //false
console.log(kite.country === kite.__proto__.country); //false
kite.hasOwnProperty("color") //false,kite 上没有 color属性
// 说明 kite.color 访问的是原型链上的属性
console.log(kite.color === kite.__proto__.color); //true
// 构造函数的原型对象 contructor 属性,指向构造函数本身
console.log(person.prototype.constructor === person); //true
Function
JS 新建一个函数一般有两种方式,funtion person(){}
和 const person = new funtion()
他们是一样的。从后者我们可以看出,创建一个函数也是 new 出来,它的构造函数其实就是 Function
。
//我们知道 javascript 中万物皆为对象,person 也是一个对象, person 可以理解成是 Function 的实例对象
//person 的 __proto__ 指向 Function 的原型也就是 Function.prototype
console.log(person.constructor === Function); // true,可以理解为 person 是从 Function 中 new 出来的(var person = new Function())
console.log(person.__proto__ === Function.prototype); //true
和普通的函数不同,Function
的__proto__
属性指向它的原型本身
// Function比较特殊, Function.__proto__ 和 Function.prototype 都指向 Function 的原型对象
console.log(Function.__proto__ === Function.prototype); //true
Object.prototype ———— 万物皆为对象
// kite.__proto__.__proto__ 和 person.__proto__.__proto__ 都指向 Object.prototype 也就是 Object 的原型对象
// 这也印证了 javascript “万物皆为对象” 的说法
console.log(kite.__proto__.__proto__ === person.__proto__.__proto__); //true
console.log(kite.__proto__.__proto__ === Object.prototype); //true
console.log(person.__proto__.__proto__ === Object.prototype); //true
值得注意的是,原型链的尽头是 null
//Object.prototype __proto__ 是 null
console.log(kite.__proto__.__proto__.__proto__); //原型链的尽头就是null
//正常我们访问一个对象的属性时,就是沿着 __proto__一步步往上找的,直到找到为止,如果一直找不到这个属性那就返回 undefined
完整原型链关系图
完整代码demo :codesandbox.io/s/wonderful…
结语
文章如果有帮到你,也请施主多多点赞,鼓励一下小僧