本文转载自微信公众号《狗狗的前端世界》,作者西岭。转载本文请联系Gogo前端世界公众号。回想一下什么是对象:Coding的第一个含义:面向对象编程JavaScript语言本身的设计缺陷,成为了对“原始世界”解释最透彻的计算机编程语言;——希灵《凡人凡语》万物皆对象(Everythingisanobject),在JS语言中一切都被视为对象。JavaScript语言的对象系统并不是基于“类”来创建对象,而是基于构造函数和原型链。简单的创建一个对象,我们可以直接通过newObject()来创建:varperson=newObject()person.name='Jack'person.age=18person.sayName=function(){console.log(this.name)}literalmethod每次都通过newObject()创建一个对象比较麻烦,所以可以通过它的简写objectliteral来创建:varperson={name:'Jack',age:18,sayName:function(){console.log(this.name)}}构造函数JavaScript语言使用构造函数作为对象的模板。所谓“构造函数”就是一个普通的函数,但是我们专门用来生成对象的,而这种方式使用的函数就是构造函数。它提供了描述对象基本结构的模板。一个可以生成多个对象的构造函数,所有对象都具有相同的结构。functionPerson(name,age){this.name=namethis.age=agethis.sayName=function(){console.log(this.name)}}varp1=newPerson('Jack',18)p1.sayName()//=>Jackvarp2=newPerson('Mike',23)p2.sayName()//=>Mike解析构造函数代码的执行在上面的例子中,Person实例对象是使用new操作符创建的;这样调用构造函数会经过以下5个步骤:创建一个空对象作为要返回的对象实例。将这个空对象的原型指向构造函数的原型属性。首先请记住,我们将讨论将这个空对象分配给函数内的this关键字。执行构造函数中的代码。returnnewobject(this)functionPerson(name,age){//当使用new操作符调用Person()时,这里实际上会先创建一个对象//然后让内部的this指向新创建的对象//继续所有对this的操作实际上是对新创建的对象进行操作this.name=namethis.age=agethis.sayName=function(){console.log(this.name)}//函数结束时,This返回,即这个新对象}构造函数与实例对象的关系构造函数是从具体事物抽象出来的抽象模板,实例对象是根据抽象构造函数模板得到的具体实例对象。实例对象来自构造函数,一个构造函数可以生成很多具体的实例对象,每个实例对象都是唯一的。每个对象都有一个构造函数属性,指向创建实例的构造函数。反过来,每个对象都有它的构造函数console.log(p1.constructor===Person)//=>trueconsole.log(p2.constructor===Person)//=>trueconsole.log(p1.constructor===p2.constructor)//=>true所以我们可以通过实例对象的constructor属性来判断实例与构造函数的关系。构造函数的问题是将构造函数作为模板来创建对象,对象的属性和方法可以在构造函数内部定义。functionCat(name,color){this.name=name;this.color=color;this.say=function(){console.log('hello'+this.name,this.color);};}varcat1=newCat('猫','白');varcat2=newCat('猫','黑');cat1.say();cat2.say();在这个例子中,表面上看起来没什么问题,但实际这样做,却有很大的劣势。即对于每一个实例对象,name和say的内容是完全一样的。每次生成一个实例,它必须为重复的内容占用更多的内存。如果实例对象很多,会造成内存的巨大浪费。那么,同样的内容放在公共部分可以节省电脑资源吗?原型JavaScript的每个对象都继承一个父对象,父对象称为原型对象。原型也是一个对象。原型对象上的所有属性和方法都可以被子对象(派生对象)共享。当通过构造函数生成实例对象时,原型对象会自动赋值给实例对象。并且每个构造函数都有一个原型属性,即实例对象的原型对象。null没有自己的原型对象。这也意味着我们可以直接在构造函数的原型属性上,即实例对象的原型对象上,定义所有对象实例需要共享的属性和方法。functionCat(color){this.color=color;}Cat.prototype.name="cat";Cat.prototype.sayhello=function(){console.log('hello'+this.name,this.color);}Cat.prototype.saycolor=function(){console.log('hello'+this.color);}varcat1=newCat('white');varcat2=newCat('black');cat1.sayhello();cat2。说颜色();此时所有实例对象的name属性和sayhello()、saycolor方法实际上都在同一个内存地址的对象中,即构造函数的prototype属性,所以提高了运行效率,节省了内存空间.原型和原型链的构造函数的原型属性是这个构造函数new创建的所有实例对象的原型对象。所有对象都有原型对象。functionCat(name,color){this.name=name;}varcat1=newCat('猫');控制台日志(cat1.__proto__.__proto__.__proto__);而原型对象中的属性和方法可以被实例对象直接使用。每当代码读取对象的属性时,都会针对具有给定名称的属性执行搜索。搜索首先从对象实例本身开始。如果在实例中找到具有给定名称的属性,则返回该属性的值。如果没有找到,则继续查找指针指向的原型对象,并在原型对象中找到具有给定名称的属性。如果在原型对象中找到该属性,则返回该属性的值。如果还没有找到,就去原型的原型中找,以此类推。如果直到最顶层都没有找到Object.prototype,则返回undefined。而这就是多个对象实例共享原型保存的属性和方法的基本原理。一个对象的属性和方法可以在它本身或它的原型对象上定义。由于原型本身也是一个对象,有自己的原型,所以形成了一条可以向上追溯的链,称为原型链。注意,不在原型上形成多层链式搜索是一种资源浪费。内置标准库和包装对象在内置标准对象中,对象是JavaScript语言最重要的数据类型,三种基本类型——数字、字符串和布尔值——的值是自动的在一定条件下转换成对象,是原始类型的“包装器”。所谓“包装对象”,就是分别对应数值、字符串、布尔值的Number、String、Boolean三种原生对象。这三个原生对象可以将原始类型的值转(包装)成一个对象。varv1=newNumber(123);varv2=newString('abc');varv3=newBool??ean(true);typeofv1//"object"typeofv2//"object"typeofv3//"object"v1===123//falsev2==='abc'//falsev3===true//false包装对象最大的目的首先是让JavaScript对象覆盖所有的值,其次是让原始类型的值方便调用某些方法。原始类型的值可以作为对象自动调用,即调用各种对象的方法和参数。这时,JavaScript引擎会自动将原始类型的值转换为包装对象实例,并在使用后立即销毁该实例。例如,一个字符串可以调用length属性来返回字符串的长度。'abc'.length//3上面代码中abc是字符串,本身不是对象,不能调用length属性。JavaScript引擎自动将其转换为包装对象,并调用该对象的length属性。调用结束后,这个临时对象就会被销毁。这称为原始类型和实例对象的自动转换。
