1. JavaScript原型
1. Object
在谈论JS中的原型之前,有必要先来了解一下JS中的 Object。 Object作为JS的原始类型之一,它是由键值对的形式构成,键名可以为String或Symbol,键值可以为JS中的任意类型。通常创建Object的方式有三种
let obj1 = {}; // 字面量方式创建 Object
let obj2 = Object.create(); // ES5中新增的Object.create()
let obj3 = new Constructor() // 通过new 关键字来创建构造函数的实例,实例就是一个Object
而今天要说的就是通过 new Constructor() 生成Object的方式来理解原型以及原型链
2. 原型
JS中当调用 new Constructor() 之后,会生成一个 Object 。 这个Object 自动拥有 Constructor 内部的属性。
function Person(name, age) {
this.name = name;
this.age = age;
this.say = function () {
console.log(`my name is ${this.name} and I am ${this.age} years old`);
}
}
// 实例会继承构造函数的全部属性 对象的方法其实也是它的一种属性
const mike = new Person('mike', 11)
const jack = new Person('jack', 12)
console.log(mike); // Person {name: "mike", age: 11, say: ƒ}
console.log(jack); // Person {name: "jack", age: 12, say: ƒ}
mike.say() // my name is mike and I am 11 years old
jack.say() // prototype.js:12 my name is jack and I am 12 years old
而在创建Object的过程中,JS会自动的创建一个Object,且称他为prototypeObject ,并将 Constructor的prototype属性指向prototypeObject,于此同时,prototypeObject内部有一个constructor 属性指向Constructor ,所以会有
console.log(Person.prototype.constructor === Person); // true
prototypeObject便叫做Constructor的原型 ,Constructot叫做构造函数,而Object叫做实例(Instance)。对于一个构造函数函数来说可以拥有无数个实例,而每个实例仅仅只存在一个构造函数。对于实例与原型的关系来说,根据ECMAScript 标准规定:
实例内部存在一个 [[prototype]] 的属性链接到原型上,通过这个属性可以在实例中访问到原型上的属性与方法。
不过现在的主流浏览器都实现了__proto__的属性,通过访问实例的__proto__属性,可以找到原型对象:
// 在 chrome浏览器中
console.log(mike.__proto__ === Person.prototype); // true
Person.prototype.eat = function() {
console.log(`${this.name} is eating!`);
}
mike.eat() // mike is eating!
jack.eat() // jack is eating!
那么现在应该很清楚构造函数、原型以及实例之间的关系了吧,让我们用图形来总结一下:
- 通过构造函数可以创建无数个实例,一个构造函数对应众多实例,
- 一个构造函数对应一个原型,即原型与构造函数是一对一的,
- 原型与实例也是一对多的关系,众多的实例会共享原型的属性,
所以在当调用实例的某个属性时,如果实例中不存在,那么JS就会自动去实例的原型里面查找这个属性。倘若原型里面也没有找到的话,又将会发生什么样好玩的事呢?
2. 原型链
理解了第一点的原型结构,那么再来理解原型链就变得很容易了。从第一点我们可以知道当访问实例的属性时,若该属性不存在实例中,那么JS会自动的去实例原型中去查找,而我们知道,实例原型同样也是一个Object,所以按照Object的规则,它肯定也会拥有构造函数以及原型(原型的原型,听起来比较绕...),所以在实例与原型之间就形成了一条如下的链式结构
即 instance -> prototype -> prototype1 形成一条链式结构,当我们执行instance.data
时,JS引擎会
- 在 instance中查找data属性,如果instance内存在data属性,那么会返回 instance内部的data属性
- 如果在 instance中没有查找到data属性,那么会去prototype 内部查找,如果找到,那么会返回 prototype内部的data属性
- 如果在 prototype中没有查找到data属性,那么会去prototype1 内部查找,如果找到,那么会返回 prototype1内部的data属性
- 如果一直查到最顶层的原型中都没找到的话,那么会返回
underfine
以上就是 JS引擎基于原型链查找属性的行为,那么现在还有一个问题,最顶层的原型是谁?
3. 原型链的另一端
我们可以通过一个实例来一直向上查找,来看看原型链最顶层的元素,以上面的实例mike为例:
console.log(mike.__proto__) // {eat: ƒ, constructor: ƒ}
console.log(mike.__proto__.__proto__) // { constructor: ƒ Object() ... }
console.log(mike.__proto__.__proto__.__proto__) // null
上面我们可以看到在原型链最顶端的竟然是 null ,而原型为null 的实例的constructor 是JS内置的Object,那么这个以null 为原型的实例可以表示为Object.prototype 。由此可以写出一条完整的链式结构图:
所以一条JS完整的原型链就表示出来了,在访问实例属性的时候会沿着上图的顺序,从上往下依次查找直到查找到null。
总结
在JS中,原型链是比较基础的知识,同时也是比较重要的知识,js 引擎在访问对象中的属性时会沿着原型链一次一次向上查找,所以可以通过这个特性,可以使得前端很灵活的使用 JS 的继承特性来方便的实现想要的效果。
常见问题FAQ
- 免费下载或者VIP会员专享资源能否直接商用?
- 本站所有资源版权均属于原作者所有,这里所提供资源均只能用于参考学习用,请勿直接商用。若由于商用引起版权纠纷,一切责任均由使用者承担。更多说明请参考 VIP介绍。
- 提示下载完但解压或打开不了?
- 找不到素材资源介绍文章里的示例图片?
- 模板不会安装或需要功能定制以及二次开发?
发表评论
还没有评论,快来抢沙发吧!