有趣的JavaScript原型链

本文深入探讨JavaScript中的原型和继承机制,解析构造函数、实例对象与原型对象之间的关系,以及如何通过原型链实现属性的查找。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

原文地址:http://www.mollypages.org/misc/js.mp 

几个有意思的知识点:

  • 所有的实例对象都继承了创建它们的构造函数的原型对象。
  • Mozilla/Konqueror浏览器实现了一个特殊的__proto__属性来指向构造函数(用来创建属于该原型对象类型的实例对象的函数)的原型对象。
  • 别去纠结有没有这么一个小小的__proto__属性,我们的思路就是要所有的实例对象能够使用它们的构造函数所指向的原型对象。这个属性是prototype,它是JavaScript标准的一部分。prototype对象默认都有一个constructor属性反向指向了以该prototype对象作为原型的构造函数。
  • prototype对象只为构造函数创建出来的实例对象继承属性所用,构造函数自己却并不使用它(但既然该构造函数自己也是一个对象,那么它也会继承它的构造函数的原型,一般是javascript系统的Function对象)。
function Foo() { } ; 
var f1 = new Foo();

Foo.prototype.x = "hello";
f1.x   //=> hello
Foo.x //=> undefined
注意,我们使用Foo.prototype来为所有Foo函数创建出来的实例对象设置属性。我们没有使用f1.prototype来为f1设置属性,请记住,这一点非常的重要!

 

  • 默认的prototype对象可以被用户创建的对象替换掉。这样做的话,我们必须重复javascript运行时在幕后对默认的prototype对象所做的那样去手动设置constructor属性。
function foo() { } ; var f1 = new foo();
f1.constructor === foo.prototype.constructor === foo  
//replace the default prototype object
foo.prototype = new Object();
//now we have:
f1.constructor === foo.prototype.constructor === Object
//so now we say:
foo.prototype.constructor = foo
//all is well again
f1.constructor === foo.prototype.constructor === foo

 

  • 每一个的prototype对象自己都是由Object()函数(默认情况下)创建而来,所以prototype有它自己的原型那就是Object.prototype。因此无论什么类型的实例最终都会继承Object.prototype对象的属性。
  • 所有的对象会自动从原型链上读取属性,就好像这些属性就定义在这些对象中一样。
为对象设置属性会隐藏该对象的原型链上相同属性名的属性。
function foo() { } 
f1 = new foo();
f2 = new foo();
foo.prototype.x = "hello";

f1.x  => "hello"
f2.x  => "hello";

f1.x = "goodbye";   //setting f1.x hides foo.prototype.x

f1.x  => "goodbye"  //hides "hello" for f1 only
f2.x  => "hello"
  
delete f1.x
f1.x  => "hello";   //foo.prototype.x is visible again to f1.
直接改变prototype上的属性会影响所有的实例对象:
foo.prototype.x = "goodbye";
//now
f1.x  => "goodbye"
f2.x  => "goodbye";

 更多有趣的东西:

 沿着上图中的箭头走,你会发现javascript语言核心中的一些有趣的东西。(找个javascript console试试这些示例代码吧)

  • Function.__proto__指向Function.prototype。这使得:Function.constructor === Function,也就是说:Function是它自己的构造函数!
  • Object instanceof Object == true。这是因为:Object.__proto__.__proto__.constructor == Object。注意,跟Object instanceof Object不一样的是Foo instanceof Foo == false,这是因为:Foo并没有作为它原型链上的某个构造函数。
  • Function.prototype.toString是一个内置的函数,它有别于另外一个内置的函数:Object.prototype.toString
f1.toString() finds:
Object.prototype.toString

We get something like: [object ...]
 然而:
Foo.toString() first finds & uses:
Function.prototype.toString()
  
We get something like: [Function foo...]
  如果我们这样做:
delete Function.prototype.toString
Foo.toString()
  
We get something like: [object ...]
===========翻译不当之处敬请指正,在此先行谢过!===========

 

JavaScript中,“闭包”和“原型链”是非常重要的两个概念,下面我们分别对这两个知识点做一个简单的介绍。 ### 闭包 闭包是指有权访问另一个函数作用域内变量的函数。换句话说,它就是一个能够记住并访问它的词法作用域(Lexical Scope)的函数,即便这个函数是在其词法作用域之外被执行的。这使得我们可以创建一些有趣的行为模式比如私有成员等等特性非常有用处! 举个例子来说吧, ```javascript function outerFunction() { var name = "外部功能"; function innerFunction() { console.log(name); } return innerFunction; } var myClosure = outerFunction(); myClosure(); // 输出:“外部功能” ``` 这里可以看到当我们调用了outerFunction以后虽然该函数体已经结束运行但是其中包含的那个innerFunction依旧保留住了对于name标识符的有效引用关系这就是所谓的闭包现象了。 ### 原型链 每一个JavaScript对象(null除外)都会有一个叫做[[Prototype]](内部属性通常不可见),也就是我们常说的_prototype_属性指向另外一个对象即此对象的原型。当尝试读取一个不存在于当前实例自身的某个特定属性的时候解释器会沿着这条由各个节点依次连接而成链条向上搜索直到找到为止如果没有就返回undefined停止查找过程。 每个构造器都有prototype属性并且所有从那个构造出来的实体也都共享着相同的_proto__链接向那里。这样做的好处是可以让很多共有的东西不必重复定义一遍节省空间提高效率同时方便维护管理等工作开展下去呢! 下面给出一段代码演示这一机制的实际应用情况: ```javascript function Person(firstName, lastName){ this.firstName = firstName; this.lastName = lastName ; } Person.prototype.greet=function(){ alert("Hello "+this.firstName+" "+this.lastName ); }; var john=new Person("John","Doe"); john.greet();//弹出"Hello John Doe" ``` 在这个案例里面greet方法实际上是挂载给了person类别的原型上并非直接隶属于某个人员个体因此不管生成多少次新的人员记录都可以公用一套问候逻辑而不需要每一次都单独编码一次啦~
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值