前言总结:结合实例说明原型和原型链的概念,总结几种创建对象的方法和扩展原型链的方法。Javascript中有一句话,文本原型,万物皆对象。当然,这句话并不严谨。例如,null和undefined不是对象。除了这两个,可以说Javascript中的一切都是对象。Javascript对象有一个名为prototype的公共属性,属性名是_proto_。这个原型属性是对另一个对象的引用,通过它我们可以访问另一个对象的所有属性和方法。例如:letnumArray=[1,2,-8,3,-4,7];Array对象有一个指向Array.prototype的prototype属性,变量numArray继承了Array.prototype对象的所有属性和方法。这就是为什么你可以直接调用sort()这样的方法:console.log(numArray.sort());//->[-4,-8,1,2,3,7]即:numArray。__proto__===Array.prototype//其他对象(函数)(如Date()、Function()、String()、Number()等)也一样;当一个构造函数被创建时,实例对象会继承构造函数的prototype属性,这是构造函数的一个非常重要的特性。new关键字在Javascript中用于实例化构造函数。请参见以下示例:constCar=function(color,model,dateManufactured){this.color=color;this.model=model;this.dateManufactured=dateManufactured;};Car.prototype.getColor=function(){returnthis.color;};Car.prototype.getModel=function(){returnthis.model;};Car.prototype.carDate=function(){return`This${this.model}wasmanufacturedintheyear${this.dateManufactured}`}letfirstCar=newCar('红色','法拉利','1985');控制台日志(第一辆车);console.log(firstCar.carDate());在上面的例子中,方法getColor、carDate、getModel都是对象(函数)Car的方法,Car的实例对象firstCar可以继承Car原型的所有方法和属性。结论:每个实例对象都有一个私有属性_proto_,指向其构造函数(prototype)的原型对象。原型链在Javascript中,如果访问一个对象中不存在的属性或方法,首先会在其原型对象上寻找。如果原型对象上不存在,则继续在原型对象的原型对象上寻找,直到找到为止。.那么原型对象有没有尽头呢?所有对象的原型的结尾都是Object.prototype,那么对象Object.prototype的_proto_指向什么呢?答案为空。我们日常开发中使用的大部分对象的_proto_基本上都不会直接指向Object.prototype,而基本上会指向另外一个对象。例如,所有函数的_proto_都会指向Function.prototype,所有数组的_proto_都会指向Array.prototype。letprotoRabbit={color:'grey',speak(line){console.log(`The${this.type}rabbitsays${line}`);}};letkillerRabbit=Object.create(protoRabbit);killerRabbit.type="刺客";killerRabbit.speak("SKREEEE!");上述代码中,变量protoRabbit被设置为所有rabbit对象的公共属性对象集。killerRabbit的兔子通过Object.create方法继承了protoRabbit的所有属性和方法,然后给killerRabbit赋了一个type属性,再看下面的代码:letmainObject={bar:2};//createanobjectlinkedto`anotherObject`letmyObject=Object.create(mainObject);for(letkinmyObject){console.log("found:"+k);}//found:bar("bar"inmyObject);上面的变量myObject本身并没有bar属性,但是这里会顺着原型链逐层查找,直到找到或者原型链结束。如果在原型链的末尾没有找到该属性,则在访问该属性时将返回undefined。使用for...in关键字迭代对象的过程类似于上面访问属性并沿着原型链搜索的过程。它将遍历原型链上的所有属性(无论该属性是否可枚举)。letprotoRabbit={color:'grey',speak(line){console.log(`The${this.type}rabbitsays${line}`);}};letkillerRabbit=Object.create(protoRabbit);killerRabbit.type="刺客";killerRabbit.speak("SKREEEE!");上面代码访问speak的效率是很高的,但是如果我们要创建很多Rabbit对象,就得重复很多代码。这就是原型和构造函数真正发挥作用的地方。){returnthis.color;}protoRabbit.prototype。speak=function(){console.log(`The${this.type}rabbitsays${this.word}`);}letkillerRabbit=newprotoRabbit('grey','SKREEEEE!','assassin');killerRabbit。说话();如上面的代码,使用构造函数可以节省很多代码。结论:每个实例对象都有一个私有属性_proto_,指向其构造函数(prototype)的原型对象。原型对象也有自己的_proto_,层层叠叠直到一个对象的原型对象为空。这层原型就是原型链。附上原型链图:创建对象的四种方法Literalobjects这个是比较常用的方法:letobj={};构造函数的创建构造函数的创建更多的是用来实现Javascript中的继承、多态、封装等特性。functionAnimal(name){this.name=name;}letcat=newAnimal('汤姆');classclass关键字是ES6引入的新特性。它实际上是一种基于原型和原型链的语法糖。classAnimal{constructor(name){this.name=name;}}letcat=newAnimal('Tom');扩展原型链的四种方法构造函数创建上面的例子对于使用构造函数创建对象很有用。让我们看另一个实际例子:functionAnimal(name){this.name=name;}Animal.prototype={run(){console.log('running');}}letcat=newAnimal('Tom');cat.__proto__===Animal.prototype;//trueAnimal.prototype.__proto__===Object.prototype;//true优点:支持目前所有能想到的浏览器(IE5.5都可以)。这种方法速度非常快,非常符合标准,并充分利用了JIST优化。缺点:要使用此方法,必须初始化相关函数。此外,构造函数的初始化可能会给生成的对象带来不需要的方法和属性。Object.createECMAScript5中引入了一个新方法:Object.create()。可以调用此方法来创建新对象。新对象的原型是调用create方法时传入的第一个参数:vara={a:1};//a--->Object.prototype--->nullvarb=Object.create(a);b.__proto__===a;//true优点:支持目前所有非微软版本或IE9以上版本的浏览器。允许一次性直接设置__proto__属性,以便浏览器更好地优化对象。它还允许通过Object.create(null)创建一个没有原型的对象。缺点:不支持IE8以下版本;这种缓慢的对象初始化在使用第二个参数时可能会成为性能黑洞,因为每个对象的描述符属性都有自己的描述对象。在处理成百上千个对象格式的对象描述时,会导致严重的性能问题。Object.setPrototypeOf语法:Object.setPrototypeOf(obj,prototype)参数:参数名称含义obj要设置其原型的对象。prototype对象的新原型(对象或null)。vara={n:1};varb={m:2};Object.setPrototypeOf(a,b);a.__proto__===b;//真正的优势:支持所有现代浏览器和MicrosoftIE9+浏览器。允许动态操作对象的原型,甚至可以强制将原型添加到使用Object.create(null)创建的没有原型的对象。缺点:该方法性能不佳,应弃用;动态设置原型会干扰浏览器对原型的优化;不支持IE8及以下浏览器版本。_proto_vara={n:1};varb={m:2};a.__proto__=b;a.__proto__===b;//true使用_proto_也可以动态设置对象的原型。优点:支持所有现代非Microsoft版本以及IE11+浏览器。将__proto__设置为非对象值会静默失败而不会引发错误。缺点:应该彻底放弃,因为这个行为根本没有任何性能;干扰原型的浏览器优化;不支持IE10及以下浏览器版本。
