网站首页 > 技术文章 正文
本篇文章给大家带来的内容是关于javascript原型链的一些知识,有需要的朋友可以参考一下,希望对你有所帮助。
先看三个面试题
第一个
function A() {} function B(a) {t his.a = a;} function C(a) {? if?(a)?{??????this.a?=?a;?? }? } A.prototype.a?=?1;B.prototype.a?=?1;C.prototype.a?=?1;?console.log(new?A().a);?console.log(new?B().a);?console.log(new?C(2).a);
第二个
Function.prototype.a = 'a'; Object.prototype.b = 'b'; function Person(){}; var p = new Person(); console.log('p.a: '+ p.a); console.log('p.b: '+ p.b); console.log('Person.a: '+ Person.a); console.log('Person.b: '+ Person.b);
第三个
function Parent() { this.a = 1; this.b = [1, 2, this.a]; this.c = { demo: 5 }; this.show = function () { console.log(this.a , this.b , this.c.demo ); }} function?Child()?{?? this.a?=?2;? this.change?=?function?()?{?? this.b.push(this.a);? this.a?=?this.b.length;?? this.c.demo?=?this.a++;?? }?}? Child.prototype?=?new?Parent(); var?parent?=?new?Parent();? var?child1?=?new?Child();? var?child2?=?new?Child();? child1.a?=?11; child2.a?=?12;? parent.show();? child1.show(); child2.show(); child1.change();? child2.change(); parent.show(); child1.show(); child2.show();
先不要看答案 看看自己能答对几题
答案之前 我们先来说一下五条原型原则
- 所有的引用类型(数组,对象,函数)都具有对象特性,即可自由扩展属性(除了null以外)
- 所有的引用类型(数组,对象,函数),都有一个__proto__属性,属性值是一个普通的对象 (隐式原型)
- 所有的函数,都有一个prototype属性,属性值也是一个普通的对象
- 所有的引用类型(数组,对象,函数),__proto__属性值值向他构造函数的prototype属性值
- 当试图得到一个对象的某个属性时,如果这个对象本身没有这个属性,那么会去它的__proto__(即它的构造函数的prototype)中寻找
好了 答案来了
第一题答案
1
undefined
2
答对了么
1,new A().a
首先 new A()中没有 a属性 那么依照原则5 去他的__proto__中查找 而他的__proto__指向的是他构造函数的prototype属性即 A.prototype.a所以 输出 1
2,new B().a
new B()对象的构造函数已经为它创建了一个a属性 这个a属性的值为传入的参数,但是此次没有为构造函数传入参数 所以最后输出为 undefined
3,new C(2).a
构造函数C判断了创建实例时有没有传入参数 如果传入 则a属性为传入的参数 如果没有参数则不创建,这里传入了2 所以最后输出2
如果这里没有传入参数 那么输出的就是对象实例原型链中的a属性的值 结果为1
第二题答案
p.a: undefined
p.b: b
Person.a: a
Person.b: b
首先p.a与p.b的输出
p为Person 实例 此时p中没有属性a与b 所以沿着原型链向上查找p.__proto__指向Person.prototype 没找到, 继续向上 p.__proto__.__proto__即Person.prototype.__proto__ 此时 Person.prototype的构造函数是Object,而Person.prototype.__proto__指向的就是Object.prototype 故此时 p.b输出为‘b’,p.a 在原型链中一直未找到 所以为undefined
Person.a与Person.b的输出
在这个题 Person为构造函数他是内置对象Function的实例对象,即 Person.__proto__为 Function.prototype 故Person.a 输出为"a",
而在Person实例与__proto__中都没有找到b属性 此时会沿着原型链继续向上查找
而Person.__proto__._proto_(即Function.prototype._proto_)为 Object.prototype
所以 Person.b 输出为"b"
第三题答案
这个题涉及的知识点比较多
- this的指向
- 原型机原型链
- 类的继承
- 原始类型和引用类型的区别
解题需要的知识点细节除了上面讲到的五条原则外还有几个
1.实例生成时,会在内存中产生一块新的堆内存,对实例的一般操作将不影响其他实例,因为在堆内存里占据不同空间,互不影响;
2.this的指向问题,常见的情况包含如下几种:
2.1 作为对象方法时,谁调用就指向谁(本题中主要涉及这一条)
2.2 作为函数调用时,指向全局顶层变量window
2.3 作为构造函数调用时,即new操作符生成实例时,构造函数中的this指向实例
2.4 call和apply方法中,显示指定this的绑定为指定上下文
3.字面量的方式(也有资料将literal翻译为直接量,个人认为后一种翻译其实更直观更形象)进行对象和数组赋值(数组本质也是对象)时,都是引用,即在堆内存生成资源,在栈内存生成变量,然后变量指向资源的地址。
4.赋值语句对于原始值赋值和引用类型赋值时的细节区别.
开始剖题
1.parent.show()
基本没什么可解释的。
直接取值就能得出答案1 [1,2,1] 5;
2.child1.show()
Child的构造函数原本是指向Child的
题目中显式将Child类的原型对象指向了Parent类的一个实例,这是javascript面向对象编程中常见的继承方式之一。此处需要注意Child.prototype指向的是Parent的实例parent,而不是指向Parent这个类
接在控制台操作输出答案可得11 [1,2,1] 5
此处令人迷惑的是this.b指向的数组最后一列为什么是1而不是11?
先来看一下child1的样子:
当执行child1.show()这个方法时,由于child1作为Child的实例,是拥有a这个属性的,所以show()方法中的this.a会直接指向这个属性的值,也就是11,而不会继续沿原型链取到__proto__所指的对象上的a属性;
接着寻找this.b,由于child1是没有b这个属性的,所以会沿原型链取到parent上的b属性,其值是一个数组,前2项是常量没什么好说的,数组的最后一项是一个引用,而此处的指针并不是一个动态指向,因为在new Parent()这一步的时候它已经被执行过一次,确定指向了parent.a所指向的资源,也就是child1.__proto__中的a属性所指向的资源,即数值1。
3.child2.show()
如果理解了上面的解释,那么此处同理即可得出答案:12 [1,2,1] 5
接着代码执行了:child1.change(); child2.change();
4.parent.show()
parent是一个Parent类的实例,Child.prorotype指向的是Parent类的另一个实例,两者在堆内存中是两份资源,互不影响,所以上述操作不影响parent实例,
输出结果保持不变:1 [1,2,1] 5;
5.child1.show(),child2.show()
child1执行了change()方法后,发生了怎样的变化呢?
this.b.push(this.a)
由于this的动态指向特性,this.b会指向Child.prototype上的b数组,this.a会指向child1的a属性,所以Child.prototype.b变成了[1,2,1,11];
this.a = this.b.length
这条语句中this.a和this.b的指向与上一句一致,故结果为child1.a变为4;
this.c.demo = this.a++
由于child1自身属性并没有c这个属性,所以此处的this.c会指向Child.prototype.c,this.a值为4,为原始类型,故赋值操作时会直接赋值,Child.prototype.c.demo的结果为4,而this.a随后自增为5(4 + 1 = 5).
接着,child2执行了change()方法, 而child2和child1均是Child类的实例,所以他们的原型链指向同一个原型对象Child.prototype,也就是同一个parent实例,所以child2.change()中所有影响到原型对象的语句都会影响child1的最终输出结果
this.b.push(this.a)
由于this的动态指向特性,this.b会指向Child.prototype上的b数组,this.a会指向child2的a属性,所以Child.prototype.b变成
[1,2,1,11,12];
this.a = this.b.length
这条语句中this.a和this.b的指向与上一句一致,故结果为child2.a变为5;
this.c.demo = this.a++
由于child2自身属性并没有c这个属性,所以此处的this.c会指向Child.prototype.c,故执行结果为Child.prototype.c.demo的值变为child2.a的值5,而child2.a最终自增为6(5 + 1 = 6).
接下来执行输出命令,最终结果将输出:
child1.show():5 [1,2,1,11,12] 5
child2.show():6 [1,2,1,11,12] 5
猜你喜欢
- 2024-11-10 这样理解 JS 原型链,通透 js原型链的理解
- 2024-11-10 JavaScript-原型链 javascript 原型,原型链 ? 有什么特点?
- 2024-11-10 javascript原型链 js原型链的用处
- 2024-11-10 js 原型/原型链/构造函数/实例/继承
- 2024-11-10 【JavaScript 高级】深入了解原型链
- 2024-11-10 快速读懂JavaScript中的原型链 js的原型和原型链是什么
- 2024-11-10 你可能不太理解的JavaScript - 原型与原型链
- 2024-11-10 我在jacascript中学习到的那些原型链,你知道吗?
- 2024-11-10 JavaScript中的原型prototype和__proto__的区别及原型链概念
- 2024-11-10 面试问题分享 - 5:解释一下 原型、构造函、实例、原型链 之间的关系
- 标签列表
-
- content-disposition (47)
- nth-child (56)
- math.pow (44)
- 原型和原型链 (63)
- canvas mdn (36)
- css @media (49)
- promise mdn (39)
- readasdataurl (52)
- if-modified-since (49)
- css ::after (50)
- border-image-slice (40)
- flex mdn (37)
- .join (41)
- function.apply (60)
- input type number (64)
- weakmap (62)
- js arguments (45)
- js delete方法 (61)
- blob type (44)
- math.max.apply (51)
- js (44)
- firefox 3 (47)
- cssbox-sizing (52)
- js删除 (49)
- js for continue (56)
- 最新留言
-