JavaScript原型和原型链

JavaScript原型和原型链

JavaScript 常被描述为一种基于原型的语言 (prototype-based language)——每个对象拥有一个原型对象,对象以其原型为模板、从原型继承方法和属性。原型对象也可能拥有原型,并从中继承方法和属性,一层一层、以此类推。这种关系常被称为原型链 (prototype chain),它解释了为何一个对象会拥有定义在其他对象中的属性和方法

原型简单来说就是函数的一个名为prototype的属性,这是所有函数都有的一个属性,如下图
函数的prototype属性
我们将自己定义的add函数的prototype属性打印出来,它的值是一个对象,里面默认有一个constructor属性,指向当前函数,也就是构造函数,除了constructor还有一个属性__proto__,这个后面会提到。这个prototype的作用是什么呢?我们看下面的代码

function Student(name){
    this.name = name;
}

var stu = new Student('小明');
Student.prototype.age = 18;
console.log(stu.age) //18

我们定义了一个函数Student并且实例化了一个对象stu,我们可以看到在Student函数中是没有定义age属性的,但是我们却可以访问到stu的age属性。关键就是Student.prototype.age = 18这一句,我们在原有的函数prototype对象上添加了一个属性age,当stu访问age时发现在它那里没有,它就会到Student的prototype对象上找,最终在prototype这里找到了age属性。那你也许就会问了,为什么不把age直接定义到函数里头,非要再弄一个prototype属性?我们考虑下面的代码

function Student(name){
    this.name = name;
    this.getName = function(){
        return this.name;
    }
}

var stu1 = new Student('小明');
var stu2 = new Student('小红');
console.log(stu1.getName()); //小明
console.log(stu2.getName()); //小红
console.log(stu1.getName === stu2.getName); //false

我们看到函数正常打印了两个对象的name属性,但是两个对象调用的方法却不是同一个。这意味着,如果我们有100对象,也就有100个不同的方法,并且这些方法的功能都是相同的。这不仅浪费内存,而且没有任何意义。要解决这种局面就要用到刚刚说的prototype,我们将上面的代码进行修改

function Student(name){
    this.name = name;
}

Student.prototype.getName = function(){
    return this.name;
}

var stu1 = new Student('小明');
var stu2 = new Student('小红');
console.log(stu1.getName()); //小明
console.log(stu2.getName()); //小明
console.log(stu1.getName === stu2.getName); //true

我们将方法加到prototype对象上,这时候两个对象所调用的方法便是同一个了。这里简单做个总结:每个函数都会有prototype属性,也就是我们所谓的原型,这个属性的值是一个对象,所有由该函数实例化的对象都可以访问prototype上的属性和方法,并且这些属性和方法是唯一的。

说完原型,我们再来看看什么是原型链,我们在写js代码的时候,常常会用到一些我们没有定义过的函数,比如数组的toString函数,这个属性并没有在Array函数中定义,你有没有想过这些函数是从哪里来的,例如

var  arr = [1,2,3];
console.log(arr.toString()) // 1,2,3

当我们用obj.xxx访问一个对象的属性时,JavaScript引擎先在当前对象上查找该属性,如果没有找到,就到其原型对象上找,如果还没有找到,就一直上溯到Object.prototype对象,最后,如果还没有找到,就只能返回undefined。

我们看看arr对象的原型链:
arr ----> Array.prototype ---->Object.prototype ----> null
解释一下为什么arr的原型链长这样,首先js的所有的数组对象都是由Array函数所实例化的,所以arr指向Array.prototype;接着Array函数是一个对象,js里一切皆对象,包括函数也是对象,所以Array.prototype指向了Object.prototype。

如果要验证这条链的话,可以使用上面提到的__proto__这个属性。如果一个对象由一个函数所实例化,那这个对象的__proto__会指向创建这个对象函数的prototype,可以试试一下代码

var  arr = [1,2,3];
console.log(arr.__proto__ === Array.prototype) //true
console.log(arr.__proto__.__proto__ === Object.prototype) //true
console.log(arr.__proto__.__proto__.__proto__ === null) //true

当我们访问arr的toString属性时,会先在arr对象上找,发现arr并没有这个属性,接着到Array.prototype上找,发现还是没有这个属性,最后到Object.prototype上找,最终找到了这个属性。如果最后Object.prototype上也没有找到,就只能显示undefined了。我们可以查看一下Object有哪些属性,可以看到Object确是有toString属性
Object属性
参考资料:廖雪锋老师的博客王福朋的博客(推荐一看)

本文简单介绍了原型和原型链,如果有不对的地方,欢迎指出

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值