新手理解的JS原型链
栏目: JavaScript · 发布时间: 5年前
内容简介:一直以来对于JavaScript 的原型链的概念,始终有些东西有一种模糊感,最近刚好有时间就塌下心认真的把《JavaScript高级程序设计》中相关内容认真读了一遍,也查看了很多网上很多资料,以前很多不明白的地方也渐渐明白了起来。写一篇文章记录一下最近学习的感悟。我们通常创建一个对象无非就两种方式:
一直以来对于JavaScript 的原型链的概念,始终有些东西有一种模糊感,最近刚好有时间就塌下心认真的把《JavaScript高级程序设计》中相关内容认真读了一遍,也查看了很多网上很多资料,以前很多不明白的地方也渐渐明白了起来。
写一篇文章记录一下最近学习的感悟。
字面量创建对象
我们通常创建一个对象无非就两种方式:
1. var obj= new Object();//new 一个Object的实例 2. var obj= {};//对象字面量
使用对象字面量 和使用new的方式是一样的。
为了简便,一般推荐使用使用字面量: var o= {};
构造函数创建对象
当我们想要创建自定义的对象时,需要用到构造函数。
构造函数和普通函数有两个区别:
1. 便于和普通函数区分,函数名首字母大写。 2. 使用 `new` 操作符调用,返回一个实例对象。
除此之外和普通函数一摸一样。
我们使用构造函数 Person
来创建两个实例对象:
function Person(name){ this.name = name; this.sayName= function (){ alert(this.name) } } var person1 = new Person('小明'); var person2 = new Person('小红'); console.log(person1);//{name: "小明", sayName: fun} console.log(person2);//{name: "小红", sayName: fun}
上面的例子不难理解,虽然这两个实例对象都有 sayName
方法,而且他们两个的作用也是一样的,但却是两个方法,只是名字和作用一样。
画个图表示一下:
如果还不明白,我在打个比喻:就像A街上有一间麦当劳,在B街上也开了一间麦当劳,它们都叫麦当劳,作用也是一样的。但是你总不能说他们是一间麦当劳吧?
person1.sayName === person2.sayName;//false
如果这样的话,我们每构造出来一个对象,都要单独为这个对象创建出一个专属于它自己使用的 sayName
,这是很占用内存的。
那我们能不能让所有的实例对象都共同使用一个 sayName
方法,来节省内存,提升效率呢?这需要我们先理解原型对象的概念。
原型对象
我们先了解原型对象的概念。
每个对象都有原型对象(null除外),我们用 __proto__
表示,每个函数都有 prototype
属性,指向实例的原型对象。
对照这句话,按照我们上面的例子,也就是说 Person.prototype
指向 person1
的原型对象( __proto__
),
Person.prototype === person1.__proto__; // true
为了便于理解,来看一张图。
恩~他们的关系大概就是这样。
原型链
原型链简单用一句话概括就是:
原型链就是 对象的 __proto__
所连接的链状结构
为了方便我们理解原型链,举一个简单的例子:
function F(){ this.a = 1; this.b = 2; } F.prototype.b = 3; F.prototype.c = 4; var o = new F();// {a: 1, b: 2} //原型链: //o --> o.__proto__ --> o.__proto__.__proto__ --> null // 其中的 --> 就表示 __proto__ 也就是原型链 console.log(o.a); // 1 // o上有a这个属性吗?有的,该属性的值为1 console.log(o.b); // 2 // o上有b这个属性吗?有的,该属性的值为2 // 原型上也有一个'b'属性,但是它不会被访问到.这种情况称为"属性遮蔽 " console.log(o.c); // 4 // o上有c这个属性吗?没有,那看看原型上有没有 // o.__proto__上有c这个属性吗?有的,该属性的值为4 console.log(o.d); // undefined // o上有d这个属性吗?没有,那看看原型上有没有 // o.__proto__ 上有d这个属性吗?没有,那看看它的原型上有没有 // o.__proto__.__proto__ 为 null,停止搜索 // 没有找到d属性,返回undefined。
我们画张图来表示:
图中这条红色的线就是原型链。
由此可见, 实例对象可访问自己原型对象上的属性和方法 ,额..准确来说是:
__proto__ undefined
我们先回顾一下那个 sayName
的问题:
怎么让所有的实例对象都是用一个 sayName
方法呢 。
现在我们可以使用原型对象来解决这个问题了。
我们把 sayName
方法放到实例的原型对象上面,也就是 Person.prototype
上面来供所有实例使用:
function Person(name){ this.name = name; } Person.prototype.sayName=function (){ alert(this.name); } var person1 = new Person('小明'); var person2 = new Person('小红'); person1.sayName === person2.sayName;//true
用图表示:
补充
说一下我的经历,一开始理解原型链时,一直在 prototype
、 __proto__
、 constructor
在这个三个属性中绕来绕去,为了便于理解,我把 constructor
放在最后了。
constructor
字面意思就很容易理解,构造函数的意思。
一句话解释:
每个原型对象都有一个 constructor 属性指向 关联的构造函数。
还是上面那个例子:
console.log(Person.prototype.constructor);//Person(){ fun }
需要注意的一点是,实例对象上没有 constructor
属性。
但是:
console.log(person1.constructor) ;//Person(){ fun }
得出这个结果很简单:
实例上查找不到 constructor
属性 --> 顺着 __proto__
在原型对象上找 --> 找到并返回。
刚才我们说了创建对象的两种方式:字面量创建对象和使用 new
操作符创建对象。
这两种方式创建出来的对象都会继承 Object.prototyoe
上的方法。
比如,我们使用字面量新创建一个对象 o
:
var o = {value: 1}; o.toString();//"[object Object]" //查找过程: o --> o.__proto__ 找到返回 o.__proto__ === Object.prototype;//true
o
这个的对象本身并没有 toString
这个方法,但它却可以使用 toString
方法。
因为它继承了 Object.prototyoe
上的 toString
的方法。
null既然对象都会继承自 Object.prototype
上面的方法,那它自己的原型又是什么呢。答案是 null
Object.prototype.__prototype__ === null;//true
以上仅自己学习所得,如有不当之处 望指出。
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网
猜你喜欢:- 理解原型其实是理解原型链
- 深入理解JS原型与原型链
- 这样理解原型与原型链比较简单
- 轻松理解JavaScript原型到原型链
- 如何理解JavaScript的原型和原型链?
- 从instanceof身上深入理解原型/原型链
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。