的部分阐明了对象的继承。我们主要通过继承来实现代码抽象和代码复用,在应用层实现功能封装。Javascript的对象继承方式真是百花齐放,属性继承、原型继承、call/aply继承、原型链继承、对象继承、构造函数继承、组合继承、类继承……十几种,每一种都有详细的讲解吧需要很多时间,这里介绍一些常用的。javascript中的继承没有明文规定,而是模仿实现的。下面我们简单梳理几个有代表性的继承。原型继承ECMAScript5中引入了一个新方法:Object.create。可以调用此方法来创建新对象。新对象的原型是调用create方法时传入的参数:lettoo={a:1}letfoo=Object.create(too)console.log(foo.a)//1通过使用Object。create方法,对象也被自动添加到foo的原型中。我们可以手动模拟一个实现与Object.create相同功能的函数:lettoo={a:1}functioncreate(prot){leto=function(){}o.prototype=protreturnnewo()}letfoo=create(too)console.log(foo.a)//1或者用更直接的方式写:functionFoo(){}Foo.prototype={a:1}lettoo=newFoo()console.log(too.a)//1原型继承是基于函数的原型属性prototypechainfunctionFoo(id){this.a=1234this.b=id||0}Foo.prototype.showData=function(){console.log(`${this.a},id:${this.b}`)}functionToo(id){Foo.apply(this,arguments)}Too.prototype=newFoo()letbar=newToo(999)bar.showData()//1234,id:999上面构造函数TOO通过重新赋值prototype属性指向构造函数Foo的一个实例,然后调用Foo的Too构造函数中的constructor,从而完成构造函数Foo的功能继承。实例栏通过属性__proto__访问原型链上的共享属性和方法。类继承javascript中的类继承也称为模拟类继承。class关键字在ES6中被正式引入,以类语言的方式创建对象。从此以后,我们也可以使用抽象类来实现继承。//父类Polygon{constructor(height,width){this.height=height;this.width=宽度;}}//子类classSquareextendsPolygon{constructor(sideLength){super(sideLength,sideLength);//调用parent的messing函数}getarea(){returnthis.height*this.width;}setsideLength(newLength){this.height=newLength;this.width=newLength;}}varsquare=newSquare(2);JavaScript中没有类的概念,只有对象。虽然我们使用了class关键字,这让JavaScript看起来好像有了“类”,但表面上看到的不一定是本质。Class只是语法糖,本质是原型链。所以JavaScript中的继承只是从对象到对象的继承。相对于继承的本质,继承是让子类拥有父类的一些属性和方法。在JavaScript中,就是让一个对象拥有另一个对象的属性和方法。继承的实现有很多,这里就不一一列举了。需要注意的是,javascript引擎在原型链上查找属性是耗时的,对性能有副作用。同时,当我们遍历对象时,原型上的属性也会被枚举出来。要确定一个属性是从对象还是从原型继承的,我们可以在对象上使用hasOwnProperty方法:letfoo={a:1}foo.hasOwnProperty('a')//truefoo.hasOwnProperty('toString')//false使用hasOwnProperty方法直接检测对象上是否存在某个属性,不遍历原型链。JavaScript支持实现继承,不支持接口继承,实现继承主要依赖原型链。想一想我们前面提到的基本上就是javascript是如何实现面向对象编程的一些知识点的。不是从概念的角度,简单的说,当我们有属性和方法需要复用,或者属性需要被多个对象共享时,我们就需要考虑继承的问题。在函数级别,通常的做法是使用作用域链来实现对内部作用域和外部作用域中的属性或函数的共享访问。举个栗子~~functioncar(userName){letcolor='red'letwheelNumber=4letuser=userNameletdriving=function(){console.log(`${user}'scar,${wheelNumber}轮子在滚动...`)}letstop=function(){console.log(`${user}的车,${wheelNumber}轮子不能再滚动了,啊...`)}return{driving:driving,stop:stop}}varmaruko=car('Maruko')maruko.driving()//Maruko的车,4个轮子滚动...maruko.stop()//Maruko的车,4个轮子不能动,嘎。..varnobita=car('Nobita')nobita.driving()//大雄的车,4个轮子在转动...nobita.stop()//大雄的车,4个轮子不能动,ga...这。..我勒个去。有没有似曾相识的感觉?这实际上是一个经典的闭包。jQuery时代很多插件js库都是用这种方式来封装独立的功能。说闭包也是继承是不是有点勉强,但是javascript中的函数也是对象,闭包是通过函数的作用域链来访问上层作用域的属性和函数的。当然,像闭包这样不使用this实现私有属性比较麻烦,而且闭包只适用于单实例场景。另一个例子:name},完成晚餐`)GoToBed(name)}dinner()}functionnobita(){letname='Nobita'functionhomework(){console.log(`${name},完成作业`)GoToBed(name)}homework()}maruko()nobita()//Maruko,吃完晚饭//Maruko,去睡觉...//Nobita,做完作业//Nobita,去睡觉...如上面的栗子,它比较常见的是以面向过程的方式将公共方法抽取到上层作用域,至少我已经做了很长时间了。GoToBed函数被提取到全局对象中,函数maruko和nobita直接通过作用域链搜索GoToBed函数。这种结构松散的代码块组织方式,其实和上面的封闭包容的意思差不多。因此,基于作用域链的公共属性和方法的管理不能算是严格意义上的继承,而只能算是javascript这种编程范式的一种面向过程的代码抽象分解方式。这种范式编程是基于作用域链的,与上面提到的基于原型链的继承的本质区别在于属性查找方式的不同。全局对象窗口形成一个封闭的上下文。如果我们假设整个窗口对象是一个全局函数,那么所有创建的局部函数都是该函数的内部函数。当我们使用这个假设时,许多问题变得更加清晰。全局函数在页面关闭前一直存在,并在生存期内为内嵌函数提供执行环境。所有内嵌函数共享全局环境的读写。允许。这种函数调用是命令式的,函数组织是嵌套的。使用闭包(函数嵌套)来组织代码流是一种无模式的规范。
