JavaScript中的对象

 

JavaScript中的对象javascript

对象的概念java

JavaScript对象的描述: An object is a collection of properties and has a single prototype object. The prototype may be either an object or the null value.数组

从上面一段话中咱们能够看出,在JavaScript中,对象是一个属性的集合,而且有一个原型对象。 而这个原型自己或者为null,或者也是一个对象。浏览器

JavaScript中内置了11种对象,咱们这里关注比较特殊的两个对象FunctionObjectide

Function:  JavaScript中函数也是对象,用来构造对象的函数叫作构造函数,如 function Animal (){}, Animal就是一个构造函数,全部的函数均可以用来构造对象,用new就是构造函数,但一般咱们采用首字母大写来与普通函数区别 全部的构造函数都是由Function这个函数对象构造出来的,它构造了系统中全部的函数对象,包括用户自定义的函数对象,系统内置的函数对象,也包括它本身。能够用如下代码证实:函数

function Animal() {}; 布局

console.log(Animal instanceof Function);    // 用户自定义函数对象 this

console.log(Object instanceof Function);   //系统内置Object函数对象idea

console.log(Function instanceof Function);    //Function自己spa

console.log(Date instanceof Function);     //系统内置的Date对象

运行结果

 

 

ObjectObjectJavaScript中另一个比较重要的内置对象,全部的对象都将继承Object原型。它自己也是一个构造函数,且由Function构造。 

理解构造关系在JavaScript中十分重要,它决定了JavaScript中的原型链。对象有一个重要的属性[[prototype]] 这个[[prototype]]是一个引用,其指向构造对象自己的构造函数的prototype 为了便于表达,咱们用Mozilla定义的__proto__来表示[[prototype]](非标准,其它JavaScript引擎不必定叫__proto__)。注意不要把__proto__和prototype弄混, 全部的对象都有一个额外的属性__proto__来指向一个原型,构造函数也不例外,这个__proto__就指向其构造函数的prototype。在本文的描述中__proto__只是指向一个prototype,而prototype自己是个对象,通常用来存放函数。

ECMAScript中这样描述prototype

Each constructor is a function that has a property named “prototype” that is used to implement prototype-based inheritance and shared properties

经过上面描述咱们可知,prototype只是构造函数的一个属性,其用于实现基于原型的继承和共享属性。

下面咱们经过一段代码的内存布局图来理解对象的构造关系以及其__proto__的指向关系。

function Animal() {}; 

var dog = new Animal();

这两句简单的代码在内存中布局以下:

 

 

 

 

__proto__prototype总结

prototype prototype是在构造函数生成的时候,会默认由Object函数给其构造一个prototype,因此其prototype的__proto__指向Object的prototype,因此全部的构造函数都有prototype, 可是实例对象没有,就是你手工赋值一个prototype,那prototype也是一个普通属性,和原型没有关系。

  • prototype是一个实例对象而不是构造函数,因此其有__proto__而没有prototype
  • prototypeObject建立,因此其__proto__ 都指向Objectprototype

 __proto__ 全部对象(包括构造函数, 由于函数也是对象)都有__proto__属性,这个属性指向其构造函数的prototype。 每一个

  • FunctionFunction本身构造,其__proto__都指向Functionprototype
  • Object也由Function构造,其__proto__都指向Functionprototype
  • 构造函数也由Function构造,其__proto__指向Functionprototype(如Animal指向Functionprototype)。
  • 实例由构造函数构造,因此实例也有__proto__,而且指向其构造函数的prototype(如dog指向Animalprototype)。 

 

由于实例的__proto__指向其构造函数的prototype, 构造函数的prototype的__proto__又指向Object的prototype,因此实例能够根据这种关联找到全部prototype上的属性和方法,这种指向关系即咱们常说的原型链。

对象的建立

因为JavaScript语法的灵活性,其对象的建立有不少方式,在阅读别人源码的时候,常常会看到各类不一样的建立方法。下面咱们看看几种常见的建立对象方式的优缺点。

  1. 1.字面量方式建立

 var person = {

name:"Archer",

age:29,

job:"software engineer",

 

sayName:function(){

console.log(this.name);

}

}; 

 

person.sayName();

 

这种方式建立对象简单易用,可是不方便代码重用。

 

  1. 2.以工厂模式建立

function createPerson(name,age, job){

var o = new Object();

o.name = name;

o.age = age;

o.job = job;

o.sayName = function(){

console.log(this.name);

};

return o;

}

 

var person1 = createPerson("Archer", 29,"software engineer");

var person2 = createPerson("idda", 24,"software engineer");

 

这种方式建立也很简单,能够重用代码。

 

  1. 3.构造函数建立

function Person(name,age,job){

this.name = name;

this.age = age;

this.job = job;

this.sayName = function(){

console.log(this.name);

};

}

var person1 = new Person("Archer", 29,"software engineer");

var person2 = new Person("idda", 24,"software engineer");

 

构造函数工厂模式比:

  • ¬ 没有建立对象o
  • ¬ 直接将属性方法赋值给this对象。
  • ¬ 没有return语句。

 

构造函数模式时,建立对象实际为以下4步:

  • ¬ 建立一个新对象
  • ¬ 将构造函数的做用域赋值给新对象(this就指向这个新对象)
  • ¬ 执行构造函数中的代码(为这个新对象添加属性,方法)
  • ¬ 返回新对象。

 

构造函数和其它普通函数的区别:

  • ¬ 调用方式不一样(构造函数和普通函数没有本质区别,任何函数,用new调用就是构造函数,不经过new调用就是普通函数)

//当构造函数使用

var person = new Person("Archer", 29,"software engineer");

person.sayName();  //Archer

//普通函数使用

Person("idda",20,"Doctor");  //在浏览器中,是添加到window

window.sayName();  //idda

console.log(window.age); //20

  • ¬ 按照习俗,将构造函数首字母大写

 

构造函数的缺点:每一个方法都要在每一个实例中建立一遍:

console.log(person1.sayName === person2.sayName);   //false

 

内存布局为:

 

 

 

  1. 4.原型模式

function Person(){

}

Person.prototype.name = "Archer";

Person.prototype.age  = 28;

Person.prototype.job = "software engineer";

Person.prototype.sayName  = function(){

console.log(this.name);

};

var person1 = new Person();

var person2 = new Person();

 

咱们建立的每一个函数都有一个prototype(原型)属性, 这个属性是一个指针,指向一个prototype对象。 这个prototype对象包含全部此函数的实 例共享的属性和方法。

 


原型模式的优势对比构造函数来讲就是让全部实例共享它包含的属性和方法。

 

console.log(person1.sayName === person2.sayName); //true

 

 

 

 

 

 

执行:Console.log(person1.name);

跟踪person1的状态可见,person1没有name属性,会根据原型链去起__proto__重查找name,其值为Archer.

 

 

 

 

执行person1.name = "idda";

后跟踪其状态,可见person1增长了一个同名属性name

 

 

因此更改person1的属性,不会修改person2的属性,代码及结果以下:

 

 

执行结果

 


内存布局以下:

 

 

 

原型的简化语法

function Person(){

}

Person.prototype ={

name:"archer",

age:29,

job:"software engineer",

sayHello:function(){

console.log("hi");

}

};

 

注意简化写法constructor再也不指向Person,而是指向Object,若是须要指向Person,可手动修改:

function Person(){

}

Person.prototype ={

constructor:Person,

name:"archer",

age:29,

job:"software engineer",

sayHello:function(){

console.log("hi");

}

};

 

另一个微小的区别,其constructor[[Enumerable]]的值由false变成了true,能够代码重置

Object.defineProperty(Person.prototype,”constructor”,{

 enumerable:false,

 value:Person

 }

);

 

对原型的修改,都会反映到其指向它的实例。若是重写了对象原型后,对象和其新原型没有任何联系,得不到新原型的任何属性

function Person(){

}

var friend = new Person();

 

 

 

 

Person.prototype ={

name:”archer”,

age:29,

job:”software engineer:,

sayHello:function(){

console.log(“hi”);

}

};

firend不能访问namesayHello等属性,如图

 

 

原型模型的问题是全部实例都共享原型,对于原型中的值类型,能够访问原型中的值,可是若是修改原型中的值,则会给对象自身添加一个同名属性,当下次访问此属性的时候,则返回此属性,再也不查找做用域链。对于引用类型,若是不修改引用,而修改其堆上的值,就会致使全部实例的值都发生更改

,如:

function Person(){

}

Person.prototype.name = "Archer";

Person.prototype.age  = 28;

Person.prototype.job = "software engineer";

Person.prototype.firends = ["idda","gang"];

 

Person.prototype.sayName  = function(){

console.log(this.name);

};

var person1 = new Person();

var person2 = new Person(); 

 

 person1.firends.push("maxi");

 

下图能够看见,咱们修改的是person1,可是person2也跟着改了:

 

 

 

因此咱们不多直接用原型模式,通常来讲咱们都会采用组合模式,组合模式是JavaScript中经常使用的模式

  1. 5.组合模式  用构造函数定义私有属性,用原型模式定义函数和共享属性

function Person(name,age,job){

this.name = name;

this.age = age;

this.job = job;

this.friends = ["mike","xiaoyu","maxi"];

}

 

Person.prototype = {

constructor: Person,

sayHello:function(){

console.log(this.name);

}

}

 

var person1 = new Person("archer",20,"software engineer");

var person2 = new Person("idea",29,"peasant-worker");

 

构造函数中的属性nameagejobfriends都是自身属性,将存储在实例自己里(不在原型中),修改不会影响其它实例,而函数sayHello是共享的,存储在原型中。

 

内存布局以下:

 

 

 

对象的继承

JavaScript中对象的继承也比较灵活,咱们列举几种常见的模式,并讨论其优缺点

  1. 1.原型链继承

function SuperType(){

this.name = "parent";

}

 

SuperType.prototype.getSuperValue = function(){

return this.name;

}

 

function SubType(){

this.sub_name = "child";

}

 

//注意:此处继承了SuperType,其实是将SubType的原型改成SuperType的实例,从而拥有其原型链

SubType.prototype = new SuperType();

//注意,此方法必定要放在上面赋值语句后面,也不能采用字面量方式声明(字面量方式声明其实是建立一个Object对象),不然修改了原型的指针,没法继承其原型链。

SubType.prototype.getSubValue = function(){

return this.sub_name;

} 

var sub = new SubType(); 

console.log(sub.getSuperValue());

 

内存布局入下:

 

 

 

 

 

原型继承的缺点:

  • 全部的实例共享了引用类型的值,值类型因为赋值是新增属性,因此不会共享
  • 没法传递参数到构造函数

 

 

  1. 2.借用构造函数方式继承

function SuperType(){

this.colors = new ["blue”,”green”,”red"];

}

 

function SubType(){

//借用构造函数继承

SuperType.call(this);

 

this.sayHello = function(){

console.log("hi");

}

}

 

借用构造的问题是,方法都在构造函数内定义,没法重用代码。

 

  1. 3.组合继承:将原型链和借用构造函数组合在一块儿,思路是经过构造函数继承实例属性,经过原型链继承原型属性. javascript中最经常使用的继承模式。

function SuperType(name){

this.name = name;

this.colors = ["white","red"]

}

 

SuperType.prototype.sayHello = function(){

console.log(this.name);

}

 

function SubType(name,age){

SuperType.call(this,name);   //第二次调用

this.age = age;

}

 

SubType.prototype = new SuperType();   //第一次调用

 

SubType.prototype.constuctor = SubType;

 

SubType.prototype.sayAge = function(){

console.log(this.age);

}

 

var person = new SubType("Archer",29);

 

person.sayAge();

内存布局为:

 

 

 

 

 

 

组合继承的缺点在于调用了两次SuperType的构造函数,并且new SuperType调用了构造函数,形成内存浪费。SuperType的构造函数生产的变量会被SubType调用的callthisname)给覆盖。可用下列方法改进:

  SubType.prototype = new SuperType();   替换为:

 var F = function(){ 

 };

 F.prototype = SuperType.prototype;

 SubType.prototype = new F();

改进前:

 

 

 

改进后能够看见其__proto__指向F,因此没有给colorsname分配内存。

 

相关文章
相关标签/搜索