深入分析JS原型链以及为什么不能在原型链上使
var obj = {name: 'h5course-com'};obj.hasOwnProperty('name'); // trueobj.hasOwnProperty('toString'); // false原型链上继承过来的属性无法通过hasOwnProperty检测到,返回false。
注意虽然in能检测到原型链的属性,但是for in却不行。
资料参考:
HTML5学堂
培训机构的课件,有图示,说的很清楚,有还有其他课件,可以联系我
一、为什么不能在原型链上使用对象:
先举一个非常简单的例子,我有一个类叫Humans(人类),然后我有一个对象叫Tom(一个人)和另一个对象叫Merry(另一个人),很明显Tom和Merry都是由Humans这一个类实例化之后得到的,然后可以把这个例子写成如下代码:
function Humans() { this.foot = 2;
}Humans.prototype.ability = true;
var Tom = new Humans();var Merry = new Humans();alert(Tom.foot);//结果:2alert(Tom.ability);//结果:truealert(Merry.foot);//结果:2alert(Merry.ability);//结果:true
以上是一个非常简单的面向对象的例子,相信都能看懂,如果尝试修改Tom的属性ability,则
function Humans() { this.foot = 2;}
Humans.prototype.ability = true;
var Tom = new Humans();var Merry = new Humans();Tom.ability = false;alert(Tom.foot);//结果:2alert(Tom.ability);//结果:falsealert(Merry.foot);//结果:2alert(Merry.ability);//结果:true
以上可以看出Tom的ability属性的值改变了,但并不影响Merry的ability属性的值,这正是我们想要的结果,也是面向对象的好处,由同一个类实例化得到的各个对象之间是互不干扰的;OK,接下来给ability换成object对象又如何?代码如下:
function Humans() { this.foot = 2;
}Humans.prototype.ability = {
run : '100米/10秒',
jump : '3米'
};var Tom = new Humans();var Merry = new Humans();Tom.ability = {
run : '50米/10秒',
jump : '2米'
};
alert(Tom.ability.run); //结果:'50米/10秒'
alert(Tom.ability.jump); //结果:'2米'
alert(Merry.ability.run); //结果:'100米/10秒'
alert(Merry.ability.jump); //结果:'3米'
以上代码就是在原型链上使用了对象,但从以上代码可以看出Tom的ability属性的改变依然丝毫不会影响Merry的ability的属性,于是乎你会觉得这样的做法并无不妥,为什么说不能在原型链上使用对象?接下来的代码就会显得很不一样,并且可以完全表达出原型链上使用对象的危险性:
function Humans() { this.foot = 2;
}
Humans.prototype.ability = {
run : '100米/10秒',
jump : '3米'};var Tom = new Humans();var Merry = new Humans();Tom.ability.run = '50米/10秒';
Tom.ability.jump = '2米';alert(Tom.ability.run); //结果:'50米/10秒'
alert(Tom.ability.jump); //结果:'2米'
alert(Merry.ability.run); //结果:'50米/10秒'
alert(Merry.ability.jump); //结果:'2米'
没错,从以上代码的输出结果可以看出Tom的ability属性的改变影响到Merry的ability属性了,于是就可以明白在原型链上使用对象是非常危险的,很容易会打破实例化对象之间的相互独立性,这就是为什么不能在原型链上使用对象的原因?是的,但我想说的可不只如此,而是其中的原理,看完后面JS原型链的深层原理之后,相信你会完全明白。
在以下第二部份解释JS原型链的深层原理之前,先来明确一个概念:原型链上的属性或方法都是被实例化对象共用的,正因如此,上面的Tom.ability.run='50米/10秒',改动了原型连上的ability才导致另一个对象Merry受影响,既然如此,你可能会问Tom.ability = {......}不也是改动了原型链上的ability吗,为什么Merry没有受影响?答案是Tom.ability = {......}并没有改动原型链上的ability属性,而是为Tom添加了一个自有属性ability,以后访问Tom.ability的时候不再需要访问原型链上的ability,而是访问其自有属性ability,这是就近原则;OK,如果你仍有疑问,可以用纸笔记下你的疑问,继续往下看你会更加明白。
二、JS原型链的深层原理:
首先要引入一个名词__proto__,__proto__是什么?在我的理解里,__proto__才是真正的原型链,prototype只是一个壳。如果你使用的是chrome浏览器,那么你可以尝试使用alert(Tom.__proto__.ability.run),你发现这样的写法完全可行,而且事实上当只有原型链上存在ability属性的时候,Tom.ability其实是指向Tom.__proto__.ability的;当然,如果你跑到IE浏览器里尝试必然会报错,事实上IE浏览器禁止了对__proto__的访问,而chrome则是允许的,当然实际开发中,我并不建议直接就使用__proto__这一属性,但它往往在我们调试代码时发挥着重要作用。有人可能会问到底Tom.__proto__和Humans.prototype是什么关系,为了理清两者的关系,下面先列出三条法则:
1、对象是拥有__proto__属性的,但没有prototype;例如:有Tom.__proto__,但没有Tom.prototype。
2、类没有__proto__属性,但有prototype;例如:没有Humans.__proto__,但有Humans.prototype(这里必须纠正一下,同时非常感谢‘川川哥哥’提出这一处错处,确实是我在写到这一点的时候没有考虑清楚,事实上Humans也是Function的一个实例对象,因此Humans.__proto__===Function.prototype是绝对成立的,稍有特殊的是这时Function.prototype是指向一个Empty(空)函数,值得推敲)。
3、由同一个类实例化(new)得到的对象的__proto__是引用该类的prototype的(也就是我们说的引用传递);例如Tom和Merry的__proto__都引用自Humans的prototype。
OK,上面说过Tom.ability={......}其实并没有改变原型链上的ability属性,或者说并没有改变Tom.__proto__.ability,而是为Tom添加了一个自有的ability属性,为了说明这一点,我们再次回到以上的第三个代码块,其代码如下:
function Humans() { this.foot = 2;
}
Humans.prototype.ability = {
run : '100米/10秒',
jump : '3米'};var Tom = new Humans();var Merry = new Humans();Tom.ability = {
run : '50米/10秒',
jump : '2米'};alert(Tom.ability.run); //结果:'50米/10秒'alert(Tom.ability.jump); //结果:'2米'alert(Merry.ability.run); //结果:'100米/10秒'alert(Merry.ability.jump); //结果:'3米'
当为Tom.ability赋予新的值后,再次访问Tom.ability时就不再指向Tom.__proto__.ability了,因为这时其实是为Tom添加了自有属性ability,可以就近取值了,你可以尝试用Chrome浏览器分别alert(Tom.ability.run)和alert(Tom.__proto__.ability.run),你会发现确实存在两个不同的值
js是什么意思
JavaScript还支持面向对象编程,具有闭包、原型链等特性,可以实现一些高级的应用。在现代Web开发中,JavaScript已经成为了不可或缺的一部分,极大地提升了网页的交互体验和用户体验。除了在Web开发中的应用,JavaScript还广泛应用于移动应用、游戏开发、数据分析和机器学习等领域。可以说,JavaScript是一种功能...
dayjs源码解析(二):Dayjs 类
需要特别关注的是wrapper方法,在Dayjs类中大量应用了该方法,其实是通过date和原实例封装了一个新实例,新实例和原实例的主要区别就是关联的时间不同。Dayjs类 Dayjs类是整个dayjs库的核心,可以给其定义的实例方法分类,也可以查看官网的文档分类。解析都写在了代码的注释里:原型链 通常来说,定义在...
JS类的创建与继承
JS中,类的创建方式与函数的创建方式相同,主要区别在于一般我们创建类时,类名的首字母需要大写,同时,我们可以再类的原型上添加公共属性和方法。创建例子如下: JS类的继承主要有6种方式:原型链继承、实例继承、组合继承、原型式继承、寄生继承、寄生组合继承 创建空...
什么是javascript封装,封装的方法有几种
Java\/C++的面向对象是object - class的关系,而JS是object - object的关系,中间通过原型prototype连接,父类和子类形成一条原型链。本文通过分析JS的对象的封装,再探讨正确实现继承的方式,然后讨论几个问题,最后再对ES6新引入的类class关键字作一个简单的说明。 JS的类其实是一个函数function,由于不是典型的OOP的类,...
js进阶-面向对象
1.2 构造函数的原型对象prototype 1.3 对象的原型__proto__ (现在的写法是[[Prototype]])1.4 构造函数constructor 总结:原型对象prototype、对象原型[[Prototype]]、构造函数直接的关系 1.5 原型链 1.6 js的成员查找机制(就近原则)2、ES5实现类的继承 ES6之前并没有提供extends的继承,我们可以...
javascript是基于对象的还是面向对象的?
在javascript中所有的东西都是对象。(一切皆为对象)这个可以通过Object.prototype.toString.call(OBJ)来检验~如果说javascript面向对象,这个不是特别准确,js是一种基于原型链的语言,这个比基于对象的语言诸如JAVA,C#等更加灵活。如果说他是基于对象,这种说法我完全赞同。希望对楼主有帮助~...
js和java哪个难
解释一:JS和Java的概述 JavaScript是一种脚本语言,主要用于前端开发,用于增强网页的交互性和动态性。Java是一种面向对象的编程语言,广泛应用于后端开发、移动应用开发等多个领域。两者都是编程语言,但应用领域和使用场景有所不同。解释二:JS的难点 JavaScript的难点主要在于其异步编程、闭包以及原型链等...
async-validator源码解析(四):Schema类
解析依赖关系,了解Schema类与utils工具方法、messages.js默认消息间的相互作用。index.js文件中的Schema类及相关内容构成了async-validator的核心。Schema类是async-validator的标准接口,文档中示例步骤简洁明了:构造Schema实例、定义规则、验证数据。构造函数划分三步,关键在于原型链上的define方法,其代码较...
使用JavaScript如何实现寄生组合式继承
首先,在js中,给对象定义属性有两种方式:\/\/通过执行构造函数设置属性function A(){ this.a = 1;}\/\/通过原型设置属性A.prototype.b = 1;所以:一个类Sub要继承另一个类Super,需要继承父类的prototype下的属性,还要执行一下父类的构造函数。即一个类Sub要继承另一个类Super,既要通过原型链...
js javaScript object对象属性和所有方法及es6对象的新增方法实例_百度...
其他如Object.getOwnPropertyDescriptor()和Object.getOwnPropertySymbols()分别获取对象属性的描述符和符号属性,Object.prototype.hasOwnProperty()判断属性是否为对象自身所有。判断对象扩展性、冻结状态、原型链关系和密封性,分别通过Object.isExtensible()、Object.isFrozen()、Object.prototype.isPrototypeOf()...
钭建复方: 在刚刚接触JS原型链的时候都会接触到一个熟悉的名词:prototype;如果你曾经深入过prototype,你会接触到另一个名词:__proto__(注意:两边各有两条下划线,不是一条).以下将会围绕prototype和__proto__这两个名词解释为什么不能...
阿图什市19783148919: js对象封装是以什么为原型的 - ?
钭建复方: 是的 如 Array.prototype
阿图什市19783148919: JS 原型链继承和类继承各有什么优缺点 - ?
钭建复方: 类式继承就像java的继承一样,思想也比较简单:在子类型构造函数的内部调用超类型构造函数.原型式继承是借助已有的对象创建新的对象,将子类的原型指向父类,就相当于加入了父类这条原型链 而你的 下面这段代码不是严格意义上的类式...
阿图什市19783148919: 如何理解javascript原型链 - ?
钭建复方: js对象是基于原型,最基础的原型是object.当对某一对象取属性的时候,当发现该对象没有该属性时,回去查询该对象的原型,还没有就查询原型的原型,直到object对象没有.而对象的原型,原型的原型……就组成了一个原型链
阿图什市19783148919: 在JavaScript的原型链继承方式中,为什么子类在调用父类的构造函数时不能传参数? - ?
钭建复方: 以前我在看书时也遇到过这样的问题,找了很多资料都没有明确的解释. 我觉得,并不是语法上不能实现对构造函数的参数传递,而是这样做不符合面向对象编程的规则:对象(实例)才是属性的拥有者. 如果在子类定义时就将属性赋了值,...
阿图什市19783148919: 什么是原型链,它们的区别,在js中它们具体指什么 - ?
钭建复方: 什么是作用域链,什么是原型链.作用域是针对变量的,比如我们创建了一个函数,函数里面又包含了一个函数,那么现在就有三个作用域 全局作用域==>函数1作用域==>函数2作用域 作用域的特点就是,先在自己的变量范围中查找,如果找不...
阿图什市19783148919: javascript 什么是原型链 - ?
钭建复方: 原型链一直是个很抽象的概念,看不到,摸不着.随着最近对JavaScript进一步的学习,我对原型链有了一点理解,下面讲出来.基础知识 在JavaScript中,一共有两种类型的值,原始值和对象值.每个对象都有一个内部属性[[prototype]],我们通常...
阿图什市19783148919: javascript原型,原型链 有什么特点 - ?
钭建复方: 特点:原型链实现了继承.<br>原型链存在两个问题:<br>a 包含引用类型值的原型属性会被所有实例共享.<br>b在创建子类型时,无法向超类型的构造函数中传递参数.<br>原型链:每个继承父函数的子函数的对象都包含一个内部属性_proto...
阿图什市19783148919: js中什么是原型对象和原型链 - ?
钭建复方: 每个 JavaScript 对象内部都有一个指向其它对象的“指针”或者 “引用“, 并通过这种方式在对象之间建立了一种联系,形成了一种链式结构,我的理解这就是所谓的原型链.function F() {} // F 是一个函数,函数也是对象,而且每个函数都有...
阿图什市19783148919: 面试问js原型怎么理解 - ?
钭建复方: 一、基于原型链的继承1.继承属性 JavaScript 对象是动态的属性“包”(指其自己的属性).JavaScript 对象有一个指向一个原型对象的链.当试图访问一个对象的属性时,它不仅仅在该对象上搜寻,还会搜寻该对象的原型,以及该对象的原...