0

0

JavaScript高级程序设计(第3版)学习笔记10 再访js对象_基础知识

php中文网

php中文网

发布时间:2016-05-16 17:49:22

|

1234人浏览过

|

来源于php中文网

原创

1、对象再认识

(1)对象属性和特性

  什么是属性(Property),什么是特性(Attribute),这有什么区别?我不想也不会从语义学上去区分,对于这系列文章来说,属性就是组成对象的一个部分,广义上也包括对象的方法,而特性则是指被描述主体所具有的特征,换句话说,属性是我们可以通过编码来访问的具体存在,而特性则主要是为了便于理解概念的抽象存在,当然,特性也可以通过相应的属性来具体外化。这一小节所讲的对象属性的特性就是对对象属性特征的一个描述,主要来自于ECMA-262规范的第5版,该规范使用两个中括号的形式来描述不能直接访问的内部特性。

A、属性类型(先给属性分下类):

  • 数据属性:直接访问属性值的属性
  • 访问器属性:通过getter/setter方法来访问属性值的属性
  • 内部属性:不能通过代码直接访问的属性,只是为了规范说明目的而存在,在规范中也使用两个中括号的形式来描述

B、对象内部属性

立即学习Java免费学习笔记(深入)”;

  内部属性不能通过代码直接访问,它主要是为了描述规范,也是给ECMAScript实现者参考的,而对于开发者来说,了解这些可以便于理解一些内部机制。比如在给一个属性赋值时,在实现中会调用[[Put]]内部方法,而读取一个属性值时,则调用[[Get]]方法。

所有对象公共的内部属性 个别对象特有的内部属性
名称 规范 名称 规范 对象
[[Prototype]] Object/Null [[PrimitiveValue]] primitive Boolean|Date|Number|String
[[Class]] String [[Construct]] SpecOp(a List of any) → Object new
[[Extensible]] Boolean [[Call]] SpecOp(any, a List of any) → any|Reference call
[[Get]] SpecOp (propName) →any [[HasInstance]] SpecOp(any) → Boolean Function
[[GetOwnProperty]] SpecOp (propName) →Undefined|Property Descriptor [[Scope]] Lexical Environment Function
[[GetProperty]] SpecOp (propName) →Undefined|Property Descriptor [[FormalParameters]] List of Strings Function
[[Put]] SpecOp (propName, any, Boolean) [
复制代码 代码如下:
]
ECMAScript code Function
[[CanPut]] SpecOp (propName) → Boolean [[TargetFunction]] Object Function.prototype.bind
[[HasProperty]] SpecOp (propName) → Boolean [[BoundThis]] any Function.prototype.bind
[[Delete]] SpecOp (propName, Boolean) → Boolean [[BoundArguments]] List of any Function.prototype.bind
[[DefaultValue]] SpecOp (Hint) → primitive [[Match]] SpecOp(String, index) → MatchResult RegExp
[[DefineOwnProperty]] SpecOp (propName, PropDesc, Boolean) → Boolean [[ParameterMap]] Object  

说明:

  • 每一个对象都有一个原型对象[[Prototype]],一般我们不能在代码中直接访问这个内部属性,但可以通过Object.getPrototypeOf(object)来获取原型对象(在Firefox中可以通过__proto__来直接访问)。
  • 在Object.prototype.toString方法中,按照规范内建对象会返回包含[[Class]]的值“[object class]”,而内建对象的[[Class]]值就是相应的名称(比如Array对象的[[Class]]值为'Array'),因此可以通过Object.prototype.toString.call(value) == '[object Array]'来判断value是否是一个Array。
  • 给一个属性赋值时,后台调用[[Put]]来实现,获取一个属性值时,后台调用[[Get]]来获取。
  • 使用new操作符调用一个函数时,后台调用[[Construct]],而使用call方法来调用函数时,后台会调用[[Call]]
  • [[HasInstance]]方法返回给定参数是否是通过调用函数来创建的,和Object的方法isPrototypeOf(obj)类似。
  • 当一个函数被执行时,就会创建一个[[Scope]]对象,可以理解为[[Scope]]就是我们前面所说的活动对象,也就是说this、arguments、形参、函数内部定义的变量和函数都是的[[Scope]]对象的属性。

C、属性特性(用来描述属性的特性)

内部特性 配置属性 属性类型 数据类型 默认值 含义 备注
[[Configurable]] configurable

数据属性

访问器属性

Boolean

true

能否通过delete删除属性从而重新定义属性

能否修改属性的特性

能否把属性修改为访问器特性

一旦把属性定义为不可配置的,就不能再变为可配置的

如果为false,不能做删除、也不能修改属性特性,但是允许修改属性值

非严格模式下会忽略相应操作,严格模式下则抛出异常

[[Enumerable]] enumerable

数据属性

访问器属性

Boolean true 能否通过for-in循环返回属性 为true时可以通过for-in来枚举,否则不能通过for-in枚举
[[Writable]] writable 数据属性 Boolean true 能否修改属性的值 为false时不能修改属性值,非严格模式下会忽略相应操作,严格模式下则抛出异常
[[Value]] value 数据属性 任意类型 undefined 属性值  
[[Get]] get 访问器属性 Undefined/Function undefined 读取属性时调用的函数 为一个函数时,会无参数调用这个函数,并将返回值作为属性值返回
[[Set]] set 访问器属性 Undefined/Function undefined 写入属性时调用的函数 为一个函数时,会将传入的值作为参数调用这个函数,赋给属性

说明:

  • 配置属性是指使用下面要讲的属性定义方法时用以定义相关特性的配置项名称。
  • 对于访问器属性,[[Get]]、[[Set]]不一定都有,没有[[Get]]的属性不能读(返回undefined,严格模式下抛出异常),没有[[Set]]的属性不能写(会忽略,严格模式下抛出异常)。
  • 注意区分对象内部属性和对象属性的特性。

D、属性定义方法(用来定义属性的方法)

最常见的定义属性的方法就是直接在对象上添加属性,比如obj.name = 'linjisong',这种情况下定义的属性所具有的内部特性都是默认的,如果想定义一个值不能被修改的属性要怎么做呢?在ES中给我们提供了几个方法用于实现类似的功能。

方法名 功能说明 参数和返回值 说明 调用示例
defineProperty() 定义一个属性

(1)目标对象

(2)属性的名字

(3)属性描述符对象

&#20351;&#29992;&#23646;&#24615;&#23450;&#20041;&#26041;&#27861;&#26102; <br>[[Enumerable]] <br>[[Configurable]] <br>[[Writable]] <br>&#40664;&#35748;&#20540;&#20026;false

// 创建一个包含一个默认属性job的对象(job属性可以修改、删除、在for-in中枚举)
var person = {job:'it'};
// 添加一个不能被修改、删除的name属性
Object.defineProperty(person, 'name', {
value:'linjisong',//这里的配置属性和上面特性列表中的配置属性一致
enumerable:true
});
// 定义多个属性(数据属性year和访问器属性age)
Object.defineProperties(person, {
year:{
value : 2012,
configurable:true,
writable:true
},
age:{
get : function(){
return this.year-1983;
}
}
});

var job = Object.getOwnPropertyDescriptor(person, 'job');
console.info(job.configurable);//true,直接添加属性时默认为true

var name = Object.getOwnPropertyDescriptor(person, 'name');
console.info(name.configurable);//false,使用属性定义方法添加属性时默认为false
console.info(person.name);//linjisong
person.name = 'oulinhai';//由于不能修改,所以值不会改变,在严格模式下会抛出异常
console.info(person.name);//linjisong

person.year = 2015;
console.info(person.year);//2015
console.info(person.age);//32,在修改year的同时,也修改了age属性
defineProperties() 定义一组属性

(1)目标对象

(2)多个属性描述符组成的一个对象

getOwnPropertyDescriptor() 获取属性的特性

(1)目标对象

(2)属性的名字

(3)返回一个包括了属性特性的对象

 

注:这些方法设置或获取的属性特殊和属性的类型有关,比如数据属性只能设置[[Confirurable]]、[[Enumerable]]、[[Writable]]、[[Value]]。

(2)防篡改对象

  所谓防篡改对象,就是给对象一定级别的保护以防止在这个级别上对对象的变更,在ES5规范中,定义了依次升高的三种保护级别:

保护级别 描述 操作方法 判断方法 说明
不可扩展 不能给对象添加新属性和方法,但可以修改已有属性和方法 preventExtensions() isExtensible():不能扩展时返回false  
密封 不可扩展,并且已有成员的[[Configurable]]设置为false,不能删除属性,但可以修改属性值 seal() isSeal():被密封时返回true isSeal()为true时一定有isExtensible()为false
冻结 密封,其[[Writable]]设置为false,但如果定义了[[Set]],访问器属性仍然可写 freeze() isFrozen():被冻结时返回true isFrozen()为true时一定有isSeal()为true,isExtensible()为false

注:一旦定义成了防篡改对象,就不能撤销。

(3)对象的其它方法

名称 描述
create(prototype[,descriptors]) 创建一个具有指定原型且可选择性地包含指定属性的对象
getOwnPropertyNames(object) 返回对象的属性(方法)的名称
getPrototypeOf(object) 返回对象的原型
keys(object) 返回对象的可枚举属性(方法)的名称

这里的create(prototype[,descriptors])是一个非常有意思的方法,规范中这样描述它的行为:

[code]
①如果prototype不是Null或Object,抛出TypeError异常
②var obj = new Object()
③设置obj的内部属性[[Prototype]]为prototype
④如果descriptors存在且不为undefined,使用Object.defineProperties(obj,descriptors)来添加属性
⑤返回obj

由于一般对象的[[Prototype]]不能直接访问,可以使用函数来进行下面模拟实现:

复制代码 代码如下:

(function(){
function Base(){};
Object.create = function(prototype, descriptors){
var typeVal = typeof prototype;
if(typeVal !== null && typeVal !== 'object' && typeVal !== 'function'){
throw new TypeError('类型错误,请检查第一个参数的类型');
}

Base.prototype = prototype;
var result = new Base();
if(descriptors){
Object.defineProperties(result, descriptors);
}
return result;
};
})();

测试一下:

复制代码 代码如下:

try{
var one = Object.create(1);//异常
}catch(e){
console.info(e);//TypeError
}
var base = {
name:'linjisong',
getName:function(){
return this.name;
}
};
var two = Object.create(base);
console.info(two.name);//linjisong
console.info(two.getName());//linjisong
var three = Object.create(base, {
name:{value:'oulinhai'},
age:{value:23}
});
console.info(three.getName());//oulinhai
console.info(three.age);//23

这里实现了一个简单的继承,这也引出下一个主题。

2、原型对象

(1)原型与原型链

  每个对象都有一个原型对象,而原型对象本身也是一个对象,也有自己的原型对象,这样就形成了一个原型链直至null对象。对象的内部属性[[Prototype]]指向的就是对象的原型对象,而Object.prototype的原型对象为null。

(2)属性查找

  在访问一个对象的属性(方法)时,引擎会先查找对象本身有没有这个属性,如果有,返回这个属性值,如果没有,则查找对象的原型是否有这个属性,如果有,返回,如果没有就继续查找原型对象的原型直至最后一个原型对象。

  注意区分属性查找和和前面说过的标识符查找的异同。属性查找是沿着原型链,标识符查找是沿着作用域链,但都有一个逐级查找的过程。

(3)对象的原型对象[[Prototype]]与函数的原型属性prototype

•每一个对象都有一个原型对象,在规范中使用[[Prototype]]来表示,这个对象一般不能直接访问,但可以通过getPrototypeOf()这个方法来获取,而在Firefox中还可以通过__proto__直接访问,来验证一下:

复制代码 代码如下:

var obj = {};
console.info(obj.__proto__===Object.getPrototypeOf(obj));//true
console.info(obj.__proto__===Object.prototype);//true

•每一个函数都有一个属性prototype,这个属性是在函数定义过程中添加的,它指向的对象就是所有使用该函数创建的实例对象的原型对象。

复制代码 代码如下:

var fn = function(){};
console.info(typeof fn.prototype);//object,一旦定义了函数,就会添加prototype属性,指向原型对象
var obj1 = new fn();
console.info(fn.prototype === Object.getPrototypeOf(obj1));//true,所有使用fn创建的实例的原型对象都指向fn.prototype
var obj2 = new fn();
console.info(fn.prototype === Object.getPrototypeOf(obj2));//true
console.info(Object.getPrototypeOf(fn) === Function.prototype);//true

当然,fn本身也是一个对象,也有自己的原型对象,它的原型对象就是Function的属性prototype了(fn.__proto__===Function.prototype)。

  我们知道,每一个对象都可以访问一个属性constructor,指向创建这个对象的函数(构造函数),实际上,constructor属性只不过是构造函数的原型对象的一个属性而已,因此通过构造函数创建的实例都能够访问constructor。

复制代码 代码如下:

var fn = function fn(){};
var obj1 = new fn();
console.info(fn.constructor);//Function()
console.info(fn.prototype.constructor);//fn(),函数原型对象的constructor指向函数本身console.info(obj1.hasOwnProperty('constructor'));//false,实例本身没有constructor属性console.info(fn.prototype.constructor === obj1.constructor);//true,实例可以访问到原型对象中的constructor属性

•函数的原型对象具有动态性,即便先创建实例,后修改原型对象,也还是能够通过实例访问到对原型对象所做的变更。

复制代码 代码如下:

var fn = function fn(){};
var obj = new fn();
console.info(obj.name);//undefined
fn.prototype.name = 'linjisong';
console.info(obj.name);//linjisong


3、创建对象

创建方式 示例 说明
传统方式
<span style="COLOR: #0000ff">var</span> person = <span style="COLOR: #0000ff">new</span><span style="COLOR: #000000"> Object(); 
person.name </span>= 'linjisong'<span style="COLOR: #000000">; 
person.job </span>= 'it';
传统方式创建对象容易产生大量重复的代码
对象字面量
<span style="COLOR: #0000ff">var</span> person =<span style="COLOR: #000000"> { 
name : </span>'linjisong'<span style="COLOR: #000000">, 
job : </span>'it'<span style="COLOR: #000000"> 
};</span>
使用对象字面量创建简洁明了,非常适合创建作为函数实参的对象
工厂模式
<span style="COLOR: #0000ff">function</span><span style="COLOR: #000000"> createPerson(name, job){ 
</span><span style="COLOR: #0000ff">var</span> o = <span style="COLOR: #0000ff">new</span><span style="COLOR: #000000"> Object(); 
o.name </span>=<span style="COLOR: #000000"> name; 
o.job </span>=<span style="COLOR: #000000"> job; 
</span><span style="COLOR: #0000ff">return</span><span style="COLOR: #000000"> o; 
} 
</span><span style="COLOR: #0000ff">var</span> person = createPerson('linjisong','it');

1、工厂模式能有效解决重复代码问题。

2、但是不能判定对象的类型

构造函数模式
<span style="COLOR: #0000ff">function</span><span style="COLOR: #000000"> Person(name, job){ 
</span><span style="COLOR: #0000ff">this</span>.name =<span style="COLOR: #000000"> name; 
</span><span style="COLOR: #0000ff">this</span>.job =<span style="COLOR: #000000"> job; 
</span><span style="COLOR: #0000ff">this</span>.getName = <span style="COLOR: #0000ff">function</span><span style="COLOR: #000000">(){ 
</span><span style="COLOR: #0000ff">return</span> <span style="COLOR: #0000ff">this</span><span style="COLOR: #000000">.name; 
} 
} 
</span><span style="COLOR: #0000ff">var</span> person = <span style="COLOR: #0000ff">new</span> Person('linjisong','it');

构造函数模式能解决重复代码问题,也能够判定对象的类型

但是这种模式下创建的每个实例都有一份属性和方法的Copy

对于方法来说,每个实例都保存一份是没有必要的

使用new调用构造函数的内部步骤:

(1)创建一个新对象

传声港
传声港

AI驱动的综合媒体服务平台,提供 “媒体发稿 + 自媒体宣发 + 效果监测” 一站式服务

下载

(2)将构造函数的作用域赋给新对象(构造函数内this指向新创建对象)

(3)执行构造函数中的代码

(4)返回新对象

原型模式
<span style="COLOR: #0000ff">function</span><span style="COLOR: #000000"> Person(){} 
Person.prototype.name </span>= 'linjisong'<span style="COLOR: #000000">; 
Person.prototype.job </span>= '<span style="COLOR: #000000">it; 
Person.prototype.getName = fucntion(){ 
return this.name; 
}; 
var person = new Person();</span>

原型模式能够解决构造函数模式的方法实例有多个副本的问题

但是同时每个实例的属性也共享了,对于引用类型的属性来说

这会导致非常严重的问题,修改一个实例的属性会导致另一个实例也修改

而且也不能接受参数

<span style="COLOR: #0000ff">function</span><span style="COLOR: #000000"> Angle(){}; 
Angle.prototype.coordinate </span>= [0,0<span style="COLOR: #000000">]; 

</span><span style="COLOR: #0000ff">var</span> a1 = <span style="COLOR: #0000ff">new</span><span style="COLOR: #000000"> Angle(); 
</span><span style="COLOR: #0000ff">var</span> a2 = <span style="COLOR: #0000ff">new</span><span style="COLOR: #000000"> Angle(); 

a1.coordinate[</span>0] = 1<span style="COLOR: #000000">; 
console.info(a2.coordinate);</span><span style="COLOR: #008000">//</span><span style="COLOR: #008000">[1,0]&#20462;&#25913;a1&#20250;&#23548;&#33268;a2&#21464;&#26356;</span>
组合构造原型模式
<span style="COLOR: #0000ff">function</span><span style="COLOR: #000000"> Person(name, job){ 
</span><span style="COLOR: #0000ff">this</span>.name =<span style="COLOR: #000000"> name; 
</span><span style="COLOR: #0000ff">this</span>.job =<span style="COLOR: #000000"> job; 
} 
Person.prototype.getName </span>=<span style="COLOR: #000000"> fucntion(){ 
</span><span style="COLOR: #0000ff">return</span> <span style="COLOR: #0000ff">this</span><span style="COLOR: #000000">.name; 
}; 
</span><span style="COLOR: #0000ff">var</span> person = <span style="COLOR: #0000ff">new</span> Person('linjisong','it');

结合构造函数模式和原型模式

使用构造函数模式创建属性,每个实例保存一份

使用原型模式共享方法,所有实例共享保存一份

这是目前使用最广泛的对象创建方式

动态原型模式
<span style="COLOR: #0000ff">function</span><span style="COLOR: #000000"> Person(name, job){ 
</span><span style="COLOR: #0000ff">this</span>.name =<span style="COLOR: #000000"> name; 
</span><span style="COLOR: #0000ff">this</span>.job =<span style="COLOR: #000000"> job; 
</span><span style="COLOR: #0000ff">if</span>(!<span style="COLOR: #000000">Person.prototype.getName){ 
Person.prototype.getName </span>=<span style="COLOR: #000000"> fucntion(){ 
</span><span style="COLOR: #0000ff">return</span> <span style="COLOR: #0000ff">this</span><span style="COLOR: #000000">.name; 
}; 
} 
} 
</span><span style="COLOR: #0000ff">var</span> person = <span style="COLOR: #0000ff">new</span> Person('linjisong','it');

这种模式实际上是对于不习惯将构造函数和原型分离而引入的

在判断的时候,可以只判断其中一个属性

寄生构造函数模式
<span style="COLOR: #0000ff">function</span><span style="COLOR: #000000"> Person(name, job){ 
</span><span style="COLOR: #0000ff">var</span> o = <span style="COLOR: #0000ff">new</span><span style="COLOR: #000000"> Object(); 
o.name </span>=<span style="COLOR: #000000"> name; 
o.job </span>=<span style="COLOR: #000000"> job; 
o.getName </span>=<span style="COLOR: #000000"> fucntion(){ 
</span><span style="COLOR: #0000ff">return</span> <span style="COLOR: #0000ff">this</span><span style="COLOR: #000000">.name; 
}; 
</span><span style="COLOR: #0000ff">return</span><span style="COLOR: #000000"> o; 
} 
</span><span style="COLOR: #0000ff">var</span> person = <span style="COLOR: #0000ff">new</span> Person('linjisong','it');

工厂模式不使用new,寄生构造函数模式使用new操作符

构造函数模式不返回,寄生构造函数模式返回对象

不能使用instanceof判断类型

稳妥构造函数模式
<span style="COLOR: #0000ff">function</span><span style="COLOR: #000000"> Person(name, job){ 
</span><span style="COLOR: #0000ff">var</span> o = <span style="COLOR: #0000ff">new</span><span style="COLOR: #000000"> Object(); 
o.getName </span>=<span style="COLOR: #000000"> fucntion(){ 
</span><span style="COLOR: #0000ff">return</span><span style="COLOR: #000000"> name; 
}; 
</span><span style="COLOR: #0000ff">return</span><span style="COLOR: #000000"> o; 
} 
</span><span style="COLOR: #0000ff">var</span> person = Person('linjisong','it');

稳妥对象:不使用this和new

稳妥构造模式类似寄生构造模式,但只能通过提供的方法访问成员

不能使用instanceof判断类型

各种创建对象的模式需要根据具体情况来看,最常用的还是对象字面量和组合构造原型方式。

4、继承

在ECMAScript中,没有接口继承,只有实现继承,这些继承主要是通过原型链来实现的。像对象创建一样,下面也通过一张表格来浏览一下一些实现继承的方法。

继承方式 示例 说明
原型链
<span style="COLOR: #0000ff">function</span> Square(){<span style="COLOR: #008000">//</span><span style="COLOR: #008000">&#27491;&#26041;&#24418;</span> 
<span style="COLOR: #0000ff">this</span>.width = 10;<span style="COLOR: #008000">//</span><span style="COLOR: #008000">&#36793;&#38271;</span> 
<span style="COLOR: #0000ff">this</span>.coordinate = [0,0];<span style="COLOR: #008000">//</span><span style="COLOR: #008000">&#24038;&#19978;&#39030;&#28857;&#30340;&#22352;&#26631; </span> 
<span style="COLOR: #000000">} 
Square.prototype.getArea </span>= <span style="COLOR: #0000ff">function</span>(){<span style="COLOR: #008000">//</span><span style="COLOR: #008000">&#35745;&#31639;&#38754;&#31215;</span> 
<span style="COLOR: #0000ff">return</span> <span style="COLOR: #0000ff">this</span>.width * <span style="COLOR: #0000ff">this</span><span style="COLOR: #000000">.width; 
}; 

</span><span style="COLOR: #0000ff">function</span> ColorSquare(){<span style="COLOR: #008000">//</span><span style="COLOR: #008000">&#26377;&#39068;&#33394;&#30340;&#27491;&#26041;&#24418;</span> 
<span style="COLOR: #0000ff">this</span>.color = 'red'<span style="COLOR: #000000">; 
} 
ColorSquare.prototype </span>= <span style="COLOR: #0000ff">new</span> Square();<span style="COLOR: #008000">//</span><span style="COLOR: #008000">&#23454;&#29616;&#20102;&#32487;&#25215;</span> 
ColorSquare.prototype.getColor = <span style="COLOR: #0000ff">function</span>(){<span style="COLOR: #008000">//</span><span style="COLOR: #008000">&#33719;&#21462;&#39068;&#33394;</span> 
<span style="COLOR: #0000ff">return</span> <span style="COLOR: #0000ff">this</span><span style="COLOR: #000000">.color; 
} 

</span><span style="COLOR: #0000ff">var</span> cs = <span style="COLOR: #0000ff">new</span><span style="COLOR: #000000"> ColorSquare(); 
console.info(cs.width);</span><span style="COLOR: #008000">//</span><span style="COLOR: #008000">10</span> 
console.info(cs.getArea());<span style="COLOR: #008000">//</span><span style="COLOR: #008000">100</span> 
console.info(cs.color);<span style="COLOR: #008000">//</span><span style="COLOR: #008000">red</span> 
console.info(cs.getColor());<span style="COLOR: #008000">//</span><span style="COLOR: #008000">red</span>

1、通过修改子类型创建函数的原型实现继承。

2、通过原型给子类型添加新方法时,一定要在替换子类型原型之后添加,而后也不能通过对象字面量修改子类型的原型。

3、可以通过两种方法确定原型和实例之间的关系:只要实例原型链中出现过构造函数fn,都返回true

(1)instance instanceof fn

(2)fn.prototype.isPrototype(instance)

4、使用原型链继承时,创建子对象时无法传递参数。

5、引用类型的父类属性会被所有子类型实例共享从而产生问题:

修改一个子类型实例的引用类型属性会导致其它所有子类型实例相应的修改

<span style="COLOR: #0000ff">var</span> cs2 = <span style="COLOR: #0000ff">new</span><span style="COLOR: #000000"> ColorSquare(); 
console.info(cs2.coordinate);</span><span style="COLOR: #008000">//</span><span style="COLOR: #008000">[0,0]</span> 
cs.coordinate[1] = 1<span style="COLOR: #000000">; 
console.info(cs2.coordinate);</span><span style="COLOR: #008000">//</span><span style="COLOR: #008000">[0,1]&#65292;&#20462;&#25913;cs&#20250;&#23548;&#33268;cs2&#20063;&#20462;&#25913;</span>
借用构造函数
<span style="COLOR: #0000ff">function</span> Square(){<span style="COLOR: #008000">//</span><span style="COLOR: #008000">&#27491;&#26041;&#24418;</span> 
<span style="COLOR: #0000ff">this</span>.width = 10;<span style="COLOR: #008000">//</span><span style="COLOR: #008000">&#36793;&#38271;</span> 
<span style="COLOR: #0000ff">this</span>.coordinate = [0,0];<span style="COLOR: #008000">//</span><span style="COLOR: #008000">&#24038;&#19978;&#39030;&#28857;&#30340;&#22352;&#26631; </span> 
<span style="COLOR: #000000">} 

Square.prototype.getArea </span>= <span style="COLOR: #0000ff">function</span>(){<span style="COLOR: #008000">//</span><span style="COLOR: #008000">&#35745;&#31639;&#38754;&#31215;</span> 
<span style="COLOR: #0000ff">return</span> <span style="COLOR: #0000ff">this</span>.width * <span style="COLOR: #0000ff">this</span><span style="COLOR: #000000">.width; 
}; 

</span><span style="COLOR: #0000ff">function</span> ColorSquare(){<span style="COLOR: #008000">//</span><span style="COLOR: #008000">&#26377;&#39068;&#33394;&#30340;&#27491;&#26041;&#24418;</span> 
Square.call(<span style="COLOR: #0000ff">this</span>);<span style="COLOR: #008000">//</span><span style="COLOR: #008000">&#23454;&#29616;&#32487;&#25215;</span> 
<span style="COLOR: #0000ff">this</span>.color = 'red'<span style="COLOR: #000000">; 
} 

</span><span style="COLOR: #0000ff">var</span> cs = <span style="COLOR: #0000ff">new</span><span style="COLOR: #000000"> ColorSquare(); 
</span><span style="COLOR: #0000ff">var</span> cs2 = <span style="COLOR: #0000ff">new</span><span style="COLOR: #000000"> ColorSquare(); 
console.info(cs.coordinate);</span><span style="COLOR: #008000">//</span><span style="COLOR: #008000">[0,0]</span> 
console.info(cs2.coordinate);<span style="COLOR: #008000">//</span><span style="COLOR: #008000">[0,0]</span> 
cs.coordinate[1] = 1<span style="COLOR: #000000">; 
console.info(cs.coordinate);</span><span style="COLOR: #008000">//</span><span style="COLOR: #008000">[0,1]</span> 
console.info(cs2.coordinate);<span style="COLOR: #008000">//</span><span style="COLOR: #008000">[0,0]&#65292;&#20114;&#30456;&#29420;&#31435;&#65292;&#20462;&#25913;cs&#19981;&#24433;&#21709;cs2</span> 
<span style="COLOR: #0000ff">try</span><span style="COLOR: #000000">{ 
console.info(cs.getArea());</span><span style="COLOR: #008000">//</span><span style="COLOR: #008000">&#24322;&#24120;&#65292;&#19981;&#33021;&#35775;&#38382;&#29238;&#31867;&#21407;&#22411;&#20013;&#26041;&#27861;</span> 
}<span style="COLOR: #0000ff">catch</span><span style="COLOR: #000000">(e){ 
console.info(e);</span><span style="COLOR: #008000">//</span><span style="COLOR: #008000">TypeError</span> 
}

1、使用借用构造函数时,可以在call调用时传递参数。

2、同时也不存在引用类型共享的问题。

3、借用构造函数的缺点是,子类不能访问父类原型中定义的方法

组合继承
<span style="COLOR: #0000ff">function</span> Square(){<span style="COLOR: #008000">//</span><span style="COLOR: #008000">&#27491;&#26041;&#24418;</span> 
<span style="COLOR: #0000ff">this</span>.width = 10;<span style="COLOR: #008000">//</span><span style="COLOR: #008000">&#36793;&#38271;</span> 
<span style="COLOR: #0000ff">this</span>.coordinate = [0,0];<span style="COLOR: #008000">//</span><span style="COLOR: #008000">&#24038;&#19978;&#39030;&#28857;&#30340;&#22352;&#26631; </span> 
<span style="COLOR: #000000">} 

Square.prototype.getArea </span>= <span style="COLOR: #0000ff">function</span>(){<span style="COLOR: #008000">//</span><span style="COLOR: #008000">&#35745;&#31639;&#38754;&#31215;</span> 
<span style="COLOR: #0000ff">return</span> <span style="COLOR: #0000ff">this</span>.width * <span style="COLOR: #0000ff">this</span><span style="COLOR: #000000">.width; 
}; 

</span><span style="COLOR: #0000ff">function</span> ColorSquare(){<span style="COLOR: #008000">//</span><span style="COLOR: #008000">&#26377;&#39068;&#33394;&#30340;&#27491;&#26041;&#24418;</span> 
Square.call(<span style="COLOR: #0000ff">this</span>);<span style="COLOR: #008000">//</span><span style="COLOR: #008000">&#21019;&#24314;&#23376;&#31867;&#23454;&#20363;&#26102;&#65292;&#31532;&#20108;&#27425;&#35843;&#29992;&#29238;&#31867;&#26500;&#36896;&#20989;&#25968;</span> 
<span style="COLOR: #0000ff">this</span>.color = 'red'<span style="COLOR: #000000">; 
} 

ColorSquare.prototype </span>= <span style="COLOR: #0000ff">new</span> Square();<span style="COLOR: #008000">//</span><span style="COLOR: #008000">&#31532;&#19968;&#27425;&#35843;&#29992;</span> 
ColorSquare.prototype.getColor = <span style="COLOR: #0000ff">function</span>(){<span style="COLOR: #008000">//</span><span style="COLOR: #008000">&#33719;&#21462;&#39068;&#33394;</span> 
<span style="COLOR: #0000ff">return</span> <span style="COLOR: #0000ff">this</span><span style="COLOR: #000000">.color; 
} 

</span><span style="COLOR: #0000ff">var</span> cs = <span style="COLOR: #0000ff">new</span><span style="COLOR: #000000"> ColorSquare(); 
</span><span style="COLOR: #0000ff">var</span> cs2 = <span style="COLOR: #0000ff">new</span><span style="COLOR: #000000"> ColorSquare(); 
console.info(cs.coordinate);</span><span style="COLOR: #008000">//</span><span style="COLOR: #008000">[0,0]</span> 
console.info(cs2.coordinate);<span style="COLOR: #008000">//</span><span style="COLOR: #008000">[0,0]</span> 
cs.coordinate[1] = 1<span style="COLOR: #000000">; 
console.info(cs.coordinate);</span><span style="COLOR: #008000">//</span><span style="COLOR: #008000">[0,1]</span> 
console.info(cs2.coordinate);<span style="COLOR: #008000">//</span><span style="COLOR: #008000">[0,0]&#65292;&#20114;&#30456;&#29420;&#31435;&#65292;&#20462;&#25913;cs&#19981;&#24433;&#21709;cs2</span> 
console.info(cs.getArea());<span style="COLOR: #008000">//</span><span style="COLOR: #008000">100&#65292;&#21487;&#20197;&#35775;&#38382;</span>

1、组合继承也称为伪经典继承,是将原型链和借用构造函数两种方式结合起来的继承方式。

2、基本思想是:

(1)使用原型链实现对原型属性和方法的继承

(2)使用借用构造函数实现对实例属性的继承

3、组合继承避免了原型链和借用构造函数的缺点,融合了它们的优点,是最常用的继承方式。

4、组合继承的缺点是需要调用两次父类的构造函数

原型式继承
<span style="COLOR: #0000ff">function</span><span style="COLOR: #000000"> create(o){ 
</span><span style="COLOR: #0000ff">var</span> fn = <span style="COLOR: #0000ff">function</span><span style="COLOR: #000000">(){}; 
fn.prototype </span>=<span style="COLOR: #000000"> o; 
</span><span style="COLOR: #0000ff">return</span> <span style="COLOR: #0000ff">new</span><span style="COLOR: #000000"> fn(); 
} 

</span><span style="COLOR: #0000ff">var</span> square =<span style="COLOR: #000000"> { 
width:</span>10<span style="COLOR: #000000">, 
coordinate:[</span>0,0<span style="COLOR: #000000">] 
}; 

</span><span style="COLOR: #0000ff">var</span> cs =<span style="COLOR: #000000"> create(square); 
</span><span style="COLOR: #0000ff">var</span> cs2 =<span style="COLOR: #000000"> create(square); 
console.info(cs.coordinate);</span><span style="COLOR: #008000">//</span><span style="COLOR: #008000">[0,0]</span> 
console.info(cs2.coordinate);<span style="COLOR: #008000">//</span><span style="COLOR: #008000">[0,0]</span> 
cs.coordinate[1] = 1<span style="COLOR: #000000">; 
console.info(cs.coordinate);</span><span style="COLOR: #008000">//</span><span style="COLOR: #008000">[0,1]</span> 
console.info(cs2.coordinate);<span style="COLOR: #008000">//</span><span style="COLOR: #008000">[0,1]&#65292;&#21644;&#21407;&#22411;&#38142;&#19968;&#26679;&#65292;&#20250;&#26377;&#20849;&#20139;&#38382;&#39064;</span>

1、这种方式实际上就是前面说的模拟ES5中create函数来实现继承。

2、ES5及前面模拟的create还可以接受另外的属性描述参数。

3、和原型链与借用构造函数不同的是,这种方式需要先有一个对象,然后直接创建子对象。

前者是构造函数的继承,而后者是对象实例的继承。

4、和使用原型链继承一样,也会有引用类型实例属性的共享问题。

寄生式继承
<span style="COLOR: #0000ff">function</span><span style="COLOR: #000000"> create(o){ 
</span><span style="COLOR: #0000ff">var</span> fn = <span style="COLOR: #0000ff">function</span><span style="COLOR: #000000">(){}; 
fn.prototype </span>=<span style="COLOR: #000000"> o; 
</span><span style="COLOR: #0000ff">return</span> <span style="COLOR: #0000ff">new</span><span style="COLOR: #000000"> fn(); 
} 

</span><span style="COLOR: #0000ff">var</span> square =<span style="COLOR: #000000"> { 
width:</span>10<span style="COLOR: #000000">, 
coordinate:[</span>0,0<span style="COLOR: #000000">] 
}; 

</span><span style="COLOR: #0000ff">function</span><span style="COLOR: #000000"> colorSquare(original){ 
</span><span style="COLOR: #0000ff">var</span> s =<span style="COLOR: #000000"> create(original); 
s.color </span>= 'red'<span style="COLOR: #000000">; 
</span><span style="COLOR: #0000ff">return</span><span style="COLOR: #000000"> s; 
} 

</span><span style="COLOR: #0000ff">var</span> cs =<span style="COLOR: #000000"> colorSquare(square); 
console.info(cs.width);</span><span style="COLOR: #008000">//</span><span style="COLOR: #008000">10</span> 
console.info(cs.coordinate);<span style="COLOR: #008000">//</span><span style="COLOR: #008000">[0,0]</span>

1、首先,这里的create函数不是必需的,任何返回新对象的函数都可以。

2、其次,这种模式也有引用类型实例属性共享的问题。

3、这种方式,可以看成将上面的对象继承包装成构造函数。

寄生组合式继承
<span style="COLOR: #0000ff">function</span><span style="COLOR: #000000"> create(o){ 
</span><span style="COLOR: #0000ff">var</span> fn = <span style="COLOR: #0000ff">function</span><span style="COLOR: #000000">(){}; 
fn.prototype </span>=<span style="COLOR: #000000"> o; 
</span><span style="COLOR: #0000ff">return</span> <span style="COLOR: #0000ff">new</span><span style="COLOR: #000000"> fn(); 
} 

</span><span style="COLOR: #0000ff">function</span><span style="COLOR: #000000"> inherit(sub, sup){ 
</span><span style="COLOR: #0000ff">var</span> prototype =<span style="COLOR: #000000"> create(sup.prototype); 
prototype.constructor </span>=<span style="COLOR: #000000"> sub; 
sub.prototype </span>=<span style="COLOR: #000000"> prototype; 
} 

</span><span style="COLOR: #0000ff">function</span> Square(){<span style="COLOR: #008000">//</span><span style="COLOR: #008000">&#27491;&#26041;&#24418;</span> 
<span style="COLOR: #0000ff">this</span>.width = 10;<span style="COLOR: #008000">//</span><span style="COLOR: #008000">&#36793;&#38271;</span> 
<span style="COLOR: #0000ff">this</span>.coordinate = [0,0];<span style="COLOR: #008000">//</span><span style="COLOR: #008000">&#24038;&#19978;&#39030;&#28857;&#30340;&#22352;&#26631; </span> 
<span style="COLOR: #000000">} 
Square.prototype.getArea </span>= <span style="COLOR: #0000ff">function</span>(){<span style="COLOR: #008000">//</span><span style="COLOR: #008000">&#35745;&#31639;&#38754;&#31215;</span> 
<span style="COLOR: #0000ff">return</span> <span style="COLOR: #0000ff">this</span>.width * <span style="COLOR: #0000ff">this</span><span style="COLOR: #000000">.width; 
}; 

</span><span style="COLOR: #0000ff">function</span> ColorSquare(){<span style="COLOR: #008000">//</span><span style="COLOR: #008000">&#26377;&#39068;&#33394;&#30340;&#27491;&#26041;&#24418;</span> 
Square.call(<span style="COLOR: #0000ff">this</span><span style="COLOR: #000000">); 
</span><span style="COLOR: #0000ff">this</span>.color = 'red'<span style="COLOR: #000000">; 
} 
inherit(ColorSquare, Square); 
ColorSquare.prototype.getColor </span>= <span style="COLOR: #0000ff">function</span>(){<span style="COLOR: #008000">//</span><span style="COLOR: #008000">&#33719;&#21462;&#39068;&#33394;</span> 
<span style="COLOR: #0000ff">return</span> <span style="COLOR: #0000ff">this</span><span style="COLOR: #000000">.color; 
} 

</span><span style="COLOR: #0000ff">var</span> cs = <span style="COLOR: #0000ff">new</span><span style="COLOR: #000000"> ColorSquare(); 
console.info(cs.width);</span><span style="COLOR: #008000">//</span><span style="COLOR: #008000">10</span> 
console.info(cs.getArea());<span style="COLOR: #008000">//</span><span style="COLOR: #008000">100</span> 
console.info(cs.color);<span style="COLOR: #008000">//</span><span style="COLOR: #008000">red</span> 
console.info(cs.getColor());<span style="COLOR: #008000">//</span><span style="COLOR: #008000">red</span> 

<span style="COLOR: #0000ff">var</span> cs2 = <span style="COLOR: #0000ff">new</span><span style="COLOR: #000000"> ColorSquare(); 
console.info(cs2.coordinate);</span><span style="COLOR: #008000">//</span><span style="COLOR: #008000">[0,0]</span> 
cs.coordinate[1] = 1<span style="COLOR: #000000">; 
console.info(cs2.coordinate);</span><span style="COLOR: #008000">//</span><span style="COLOR: #008000">[0,0]</span>

1、这种方式只调用了一次父类构造函数,从而避免了在子类型的原型对象上创建不必要的属性。

2、能够保证原型链不变,从而可以正常使用instanceof和isPrototypeOf()。

相关文章

java速学教程(入门到精通)
java速学教程(入门到精通)

java怎么学习?java怎么入门?java在哪学?java怎么学才快?不用担心,这里为大家提供了java速学教程(入门到精通),有需要的小伙伴保存下载就能学习啦!

下载

本站声明:本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系admin@php.cn

热门AI工具

更多
DeepSeek
DeepSeek

幻方量化公司旗下的开源大模型平台

豆包大模型
豆包大模型

字节跳动自主研发的一系列大型语言模型

通义千问
通义千问

阿里巴巴推出的全能AI助手

腾讯元宝
腾讯元宝

腾讯混元平台推出的AI助手

文心一言
文心一言

文心一言是百度开发的AI聊天机器人,通过对话可以生成各种形式的内容。

讯飞写作
讯飞写作

基于讯飞星火大模型的AI写作工具,可以快速生成新闻稿件、品宣文案、工作总结、心得体会等各种文文稿

即梦AI
即梦AI

一站式AI创作平台,免费AI图片和视频生成。

ChatGPT
ChatGPT

最最强大的AI聊天机器人程序,ChatGPT不单是聊天机器人,还能进行撰写邮件、视频脚本、文案、翻译、代码等任务。

相关专题

更多
雨课堂网页版登录入口与使用指南_官方在线教学平台访问方法
雨课堂网页版登录入口与使用指南_官方在线教学平台访问方法

本专题系统整理雨课堂网页版官方入口及在线登录方式,涵盖账号登录流程、官方直连入口及平台访问方法说明,帮助师生用户快速进入雨课堂在线教学平台,实现便捷、高效的课程学习与教学管理体验。

2

2026.02.12

豆包AI网页版入口与智能创作指南_官方在线写作与图片生成使用方法
豆包AI网页版入口与智能创作指南_官方在线写作与图片生成使用方法

本专题汇总豆包AI官方网页版入口及在线使用方式,涵盖智能写作工具、图片生成体验入口和官网登录方法,帮助用户快速直达豆包AI平台,高效完成文本创作与AI生图任务,实现便捷智能创作体验。

36

2026.02.12

PostgreSQL性能优化与索引调优实战
PostgreSQL性能优化与索引调优实战

本专题面向后端开发与数据库工程师,深入讲解 PostgreSQL 查询优化原理与索引机制。内容包括执行计划分析、常见索引类型对比、慢查询优化策略、事务隔离级别以及高并发场景下的性能调优技巧。通过实战案例解析,帮助开发者提升数据库响应速度与系统稳定性。

4

2026.02.12

Next.js全栈开发与SSR服务端渲染实战
Next.js全栈开发与SSR服务端渲染实战

本专题系统讲解 Next.js 框架在现代全栈开发中的应用,重点解析 SSR、SSG 与 ISR 渲染模式的原理与差异。内容涵盖路由系统、API Routes、数据获取策略、性能优化以及部署实践。通过完整项目示例,帮助开发者掌握高性能 SEO 友好的 React 全栈开发方案。

1

2026.02.12

Kotlin协程编程与Spring Boot集成实践
Kotlin协程编程与Spring Boot集成实践

本专题围绕 Kotlin 协程机制展开,深入讲解挂起函数、协程作用域、结构化并发与异常处理机制,并结合 Spring Boot 展示协程在后端开发中的实际应用。内容涵盖异步接口设计、数据库调用优化、线程资源管理以及性能调优策略,帮助开发者构建更加简洁高效的 Kotlin 后端服务架构。

22

2026.02.12

2026春节习俗大全
2026春节习俗大全

本专题整合了2026春节习俗大全,阅读专题下面的文章了解更多详细内容。

254

2026.02.11

Yandex网页版官方入口使用指南_国际版与俄罗斯版访问方法解析
Yandex网页版官方入口使用指南_国际版与俄罗斯版访问方法解析

本专题全面整理了Yandex搜索引擎的官方入口信息,涵盖国际版与俄罗斯版官网访问方式、网页版直达入口及免登录使用说明,帮助用户快速、安全地进入Yandex官网,高效使用其搜索与相关服务。

775

2026.02.11

虫虫漫画网页版入口与免费阅读指南_正版漫画全集在线查看方法
虫虫漫画网页版入口与免费阅读指南_正版漫画全集在线查看方法

本专题系统整理了虫虫漫画官网及网页版最新入口,涵盖免登录观看、正版漫画全集在线阅读方式,并汇总稳定可用的访问渠道,帮助用户快速找到虫虫漫画官方页面,轻松在线阅读各类热门漫画内容。

95

2026.02.11

Docker容器化部署与DevOps实践
Docker容器化部署与DevOps实践

本专题面向后端与运维开发者,系统讲解 Docker 容器化技术在实际项目中的应用。内容涵盖 Docker 镜像构建、容器运行机制、Docker Compose 多服务编排,以及在 DevOps 流程中的持续集成与持续部署实践。通过真实场景演示,帮助开发者实现应用的快速部署、环境一致性与运维自动化。

7

2026.02.11

热门下载

更多
网站特效
/
网站源码
/
网站素材
/
前端模板

精品课程

更多
相关推荐
/
热门推荐
/
最新课程
PostgreSQL 教程
PostgreSQL 教程

共48课时 | 8.9万人学习

MongoDB 教程
MongoDB 教程

共17课时 | 2.8万人学习

C# 教程
C# 教程

共94课时 | 9.2万人学习

关于我们 免责申明 举报中心 意见反馈 讲师合作 广告合作 最新更新
php中文网:公益在线php培训,帮助PHP学习者快速成长!
关注服务号 技术交流群
PHP中文网订阅号
每天精选资源文章推送

Copyright 2014-2026 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号