关于JS原型链

相信大家都知道JS原型链最大的特点就是动态的继承。若果调用的属性或者方法在实例中没有找到的话就会动态的向它的上一层原型查找。这个特点在实践中也会经常运用到。我们再说说在运用原型链动态继承时应该注意的一些地方。

首先举一个例子:

<span style="color:#006600;">function demo(){}
    demo.prototype.TJoe = 18;

    var test = new demo();
    alert(test.TJoe);</span>
实例test中并没有TJoe,所以会向上查找,得到结果:


然后加入一句覆盖掉原来的属性的语句

<span style="color:#009900;">function demo(){}
    demo.prototype.TJoe = 18;

    demo.prototype = {优快云 : 666};      //覆盖语句

    var test = new demo();

    alert(test.TJoe);</span>
这时候动态的去查找原型得到的结果:


这时候没有找到TJoe于是返回了undefined。我们稍稍做一点变动:

<span style="color:#33cc00;">function demo(){}
    demo.prototype.TJoe = 18;

    var test = new demo();

    demo.prototype = {优快云 : 666};		//覆盖语句移到了实例创建之后

    alert(test.TJoe);</span>

会怎么样呢?

依然得到了原先的TJoe。这就奇怪了,只是改变了覆盖语句的位置,接过缺大相径庭,到底是怎么回事?好吧,我们一点点来说。

首先,什么是原型

在JavaScript 中,每当定义一个对象(函数)时候,对象中都会包含一些预定义的属性。其中函数对象的一个属性就是原型对象 prototype。

这里图会比较说明问题


对象或函数一级级继承构成了原型链,如果原型被更改,那之后继承于它的都会受到影响

好的,回过头来看看刚才的例子。实例test的原型真的被更改了吗?这里我们要着重的区分__proto__和prototype。

JS在创建对象的时候都会有__proto__这个内置属性,它用于指向原型对象prototype。所以,我们之前改掉的是对象的那个原型,而实例的__proto__依然指向着原来的原型,所以输出实例时其指向的依然是原先的原型。

搞清这个问题后我们在说说实现继承的方法。

1.直接写

test.prototype = new demo();

这样做调用了构造函数,实现了继承。但是,这时候的test还没有实例化,demo中这时候传什么进来都不太好。

2.利用ECMA5的Object.create

test.prototype = Object.create(demo.prototype);

这里利用Object.create创建了一个空的对象并指向了demo.prototype,而且test.prototype也有自己的空的对象,比较完美。

但是也有问题就是Object.create是ECMA5中的,支持ie9+,所以我们得想个办法在低版本的ie下也能支持它。

于是我们模拟一个Object.create:

<span style="color:#33cc00;">if(!Object.create){
    	Object.create = function (proto){
    		function F(){}
    		F.prototype = proto;    //赋值为原型的对象
    		return new F;
    	};
    }</span>
这样就更完美了~


Go for it~


JavaScript 中的原型链是一个非常重要的概念,理解它可以让你更好地掌握 JavaScript 的工作原理和对象之间的关系。接下来我将为你详细解释什么是原型链以及它是如何工作的。 ### 什么是原型链? 每个 JavaScript 对象都有一个内部属性称为 `[[Prototype]]`(也叫做“__proto__”,注意这不是标准属性),这个属性指向另一个对象——即它的“原型”。而该原型对象本身也有自己的原型...以此类推形成了一条链条结构,这条链条就是所谓的**原型链**。当访问对象的某个属性时,如果找不到的话就会沿着原型链向上查找,直到找到对应的属性或到达最顶端的对象 `null`(因为 `Object.prototype.__proto__ === null`)。因此可以说,所有函数实例都共享同一个构造函数所关联的原型对象。 #### 构造函数与 prototypeJavaScript 中,当你定义一个新的函数时,默认会给它添加一个名为 `prototype` 的公共属性,并且这个属性会自动成为一个新对象(我们称作原型对象)。如果你使用这个函数去创建实例化对象 (`new`) ,那么每一个生成的新对象都会有一个隐含指针指向原始函数的 `prototype` 属性: ```javascript function Person(name) { this.name = name; } // 这里给Person设置了prototype属性 Person.prototype.sayHello = function() { console.log(`Hi, I'm ${this.name}`); }; let p1 = new Person('Alice'); console.log(p1 instanceof Person); // true p1.sayHello(); // Hi, I'm Alice ``` 在这个例子中,`p1` 实际上是通过其内部链接到 `Person.prototype` 来获取方法 `sayHello()` 的引用。这也说明为什么我们可以直接修改原型上的成员而不必担心会影响已经存在的对象;同样地,只要更改了某一处原型内的内容,所有的子节点都能立即看到更新后的结果。 ### 原型链的实际应用 利用好原型机制能够带来很多好处,比如减少内存占用(只需要存储一次共有功能)、方便拓展已有库等。此外还有一些常见的模式如寄生组合式继承、工厂模式等等都可以借助原型完成高效的代码复用。然而值得注意的是滥用这种特性也可能导致性能问题甚至难以调试的问题发生,所以在日常编码实践中需要谨慎对待。 --- ### 示例代码展示原型链的关系: 假设我们现在有两个自定义类型 A 和 B,其中 B 继承自 A: ```javascript function Animal(type){ this.type = type; } Animal.prototype.eat = function(){ return `${this.type} is eating.`; }; function Dog(name){ Animal.apply(this, arguments); this.name = name; } Dog.prototype = Object.create(Animal.prototype); // 创建新的原型对象 Dog.prototype.constructor = Dog; // 恢复 constructor 引用 Dog.prototype.bark = function(){ // 添加特有行为 return `${this.name}: Woof!`; }; const myDog = new Dog("Max"); console.log(myDog.eat()); // 输出 "is eating." console.log(myDog.bark()); // 输出 "Max: Woof!" ``` 这里可以看到我们不仅可以在 `myDog` 上调用来自 `Dog.prototype` 的 `.bark()` 方法,也可以顺利调用了来源于更高级别原型的方法 `.eat()` 。这就是典型的原型链工作机制! --- 为了帮助你进一步理解和探索关于 JavaScript 原型链的知识,请考虑以下几个相关话题进行学习:
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值