JavaScript原型与继承实例分析
今天小编给大家分享一下JavaScript原型与继承实例分析的相关知识点,内容详细,逻辑清晰,相信大部分人都还太了解这方面的知识,所以分享这篇文章给大家参考一下,希望大家阅读完这篇文章后有所收获,下面我们一起来了解一下吧。
首先我们需要知道的是,JavaScript 是一种动态语言,本质上说它是没有Class
(类)的;但是它也需要一种继承的方式, 那就是原型继承;JavaScript 对象的一些属性和方法都是继承自别的对象。
很多同学对 JavaScript 的原型和继承不是很理解,一个重要的原因就是大家没有理解__proto__
和prototype
这两个属性的意思。 接下来我们先来好好梳理一下这两个属性,看看它们存在哪里,代表了什么意义,又有什么作用。
首先来说一下__proto__
这个属性吧,我们需要知道的是,除了null
和undefined
,JavaScript
中的所有数据类型都有这个属性; 它表示的意义是:当我们访问一个对象的某个属性的时候,如果这个对象自身不存在这个属性, 那么就从这个对象的__proto__
(为了方便下面描述,这里暂且把这个属性称作p0
)属性上面 继续查找这个属性,如果p0上面还存在__proto__
(p1)属性的话,那么就会继续在p1上面查找响应的属性, 直到查找到这个属性,或者没有__proto__
属性为止。
我们把一个对象的__proto__
属性所指向的对象,叫做这个对象的原型;我们可以修改一个对象的原型来让这个对象拥有某种属性,或者某个方法。
//修改一个Number类型的值的原型constnum=1;num.__proto__.name="Mynameis1";console.log(num.name);//Mynameis1//修改一个对象的原型constobj={};obj.__proto__.name="dreamapple";console.log(obj.name);//dreamapple
这里需要特别注意的是,__proto__
这个属性虽然被大多数的浏览器支持,但是其实它仅在ECMAScript 2015 规范
中被准确的定义, 目的是为了给这个传统的功能定制一个标准,以确保浏览器之间的兼容性。通过使用__proto__
属性来修改一个对象的原型是非常慢且影响性能的一种操作。所以,现在如果我们想要获取一个对象的原型,推荐使用Object.getPrototypeOf
或者Reflect.getPrototypeOf
,设置一个对象的原型推荐使用Object.setPrototypeOf
或者是Reflect.setPrototypeOf
。
到这里为止,我们来对__proto__
属性做一个总结:
存在哪里?除了null
和undefined
所有其他的JavaScript对象或者原始类型都有这个属性
代表了什么?表示了一个对象的原型
有什么作用?可以获取和修改一个对象的原型
说完__proto__
属性,接下来我们就要好好的来理解一下prototype
属性了;首先我们需要记住的是,这个属性一般只存在于函数对象上面; 只要是能够作为构造器的函数,他们都包含这个属性。也就是说,只要这个函数能够通过使用new
操作符来生成一个新的对象, 那么这个函数肯定具有prototype
属性。因为我们自定义的函数都可以通过new
操作符生成一个对象,所以我们自定义的函数都有prototype
这个属性。
//函数字面量console.log((function(){}).prototype);//{constructor:ƒ}//Date构造器console.log(Date.prototype);//{constructor:ƒ,toString:ƒ,toDateString:ƒ,toTimeString:ƒ,toISOString:ƒ,…}//Math.abs不是构造器,不能通过new操作符生成一个新的对象,所以不含有prototype属性console.log(Math.abs.prototype);//undefined
那这个prototype
属性有什么作用呢?这个prototype
属性的作用就是:函数通过使用new
操作符生成的一个对象, 这个对象的原型(也就是__proto__
)指向该函数的prototype
属性。那么一个比较简洁的表示__proto__
和prototype
属性之间关系的等式也就出来了,如下所示:
//其中F表示一个自定义的函数或者是含有prototype属性的内置函数newF().__proto__===F.prototype//true
看到上面等式,我想大家对于__proto__
和prototype
之间关系的理解应该会更深一层了。
好,接下来我们对prototype
属性也做一个总结:
存在哪里?自定义的函数,或者能够通过new
操作符生成一个对象的内置函数
代表了什么?它表示了某个函数通过new
操作符生成的对象的原型
有什么作用?可以让一个函数通过new
操作符生成的许多对象共享一些方法和属性
其实到这里为止,关于JavaScript的原型和继承已经讲得差不多了;下面的内容是一些基于上面的一些拓展, 可以让你更好地理解我们上面所说的。
当我们理解了上面的知识点之后,我们就可以对下面的表达式做一个判断了:
//因为Object是一个函数,函数的构造器都是FunctionObject.__proto__===Function.prototype//true//通过函数字面量定义的函数的__proto__属性都指向Function.prototype(function(){}).__proto__===Function.prototype//true//通过对象字面量定义的对象的__proto__属性都是指向Object.prototype({}).__proto__===Object.prototype//true//Object函数的原型的__proto__属性指向nullObject.prototype.__proto__===null//true//因为Function本身也是一个函数,所以Function函数的__proto__属性指向它自身的prototypeFunction.__proto__===Function.prototype//true//因为Function的prototype是一个对象,所以Function.prototype的__proto__属性指向Object.prototypeFunction.prototype.__proto__===Object.prototype//true
如果你能够把上面的表达式都梳理清楚的话,那么说明你对这部分知识掌握的还是不错的。
谈及JavaScript的原型和继承,那么我们还需要知道另一个概念;那就是constructor
,那什么是constructor
呢?constructor
表示一个对象的构造函数,除了null
和undefined
以外,JavaScript中的所有数据类型都有这个属性; 我们可以通过下面的代码来验证一下:
null.constructor//UncaughtTypeError:Cannotreadproperty'constructor'ofnull...undefined.constructor//UncaughtTypeError:Cannotreadproperty'constructor'ofundefined...(true).constructor//ƒBoolean(){[nativecode]}(1).constructor//ƒNumber(){[nativecode]}"hello".constructor//ƒString(){[nativecode]}
但是其实上面这张图的表示并不算准确,因为一个对象的constructor
属性确切地说并不是存在这个对象上面的; 而是存在这个对象的原型上面的(如果是多级继承需要手动修改原型的constructor
属性,见文章末尾的代码),我们可以使用下面的代码来解释一下:
constF=function(){};//当我们定义一个函数的时候,这个函数的prototype属性上面的constructor属性指向自己本身F.prototype.constructor===F;//true
关于constructor
还有一些需要注意的问题,对与JavaScript的原始类型来说,它们的constructor
属性是只读的,不可以修改。 我们可以通过下面的代码来验证一下:
(1).constructor="something";console.log((1).constructor);//输出ƒNumber(){[nativecode]}
当然,如果你真的想更改这些原始类型的constructor
属性的话,也不是不可以,你可以通过下面的方式来进行修改:
Number.prototype.constructor="numberconstructor";(1).constructor=1;console.log((1).constructor);//输出numberconstructor
当然上面的方式我们是不推荐你在真实的开发中去使用的,接下来,我会使用一些代码来把今天讲解的知识再大致的回顾一下:
functionAnimal(name){this.name=name;}Animal.prototype.setName=function(name){this.name=name;};Animal.prototype.getName=function(name){returnthis.name;};functionDog(name,breed){Animal.call(this,name);this.breed=breed;}Dog.prototype=Object.create(Animal.prototype);//因为上面的语句将我们原来的prototype的指向修改了,所以我们要重新定义Dog的prototype属性的constructor属性Reflect.defineProperty(Dog.prototype,"constructor",{value:Dog,enumerable:false,//不可枚举writable:true});constanimal=newAnimal("potato");console.log(animal.__proto__===Animal.prototype);//trueconsole.log(animal.constructor===Animal);//trueconsole.log(animal.name);//potatoconstdog=newDog("potato","labrador");console.log(dog.name);//potatoconsole.log(dog.breed);//labradorconsole.log(dog.__proto__===Dog.prototype);//trueconsole.log(dog.constructor===Dog);//true
以上就是“JavaScript原型与继承实例分析”这篇文章的所有内容,感谢各位的阅读!相信大家阅读完这篇文章都有很大的收获,小编每天都会为大家更新不同的知识,如果还想学习更多的知识,请关注亿速云行业资讯频道。
声明:本站所有文章资源内容,如无特殊说明或标注,均为采集网络资源。如若本站内容侵犯了原著者的合法权益,可联系本站删除。