发布时间:2023-11-02 16:30
声明:本人的所有博客皆为个人笔记,作为个人知识索引使用,因此在叙述上存在逻辑不通顺、跨度大等问题,希望理解。分享出来仅供大家学习翻阅,若有错误希望指出,感谢!
JavaScript中有两种属性,数据属性和访问器属性
属性特性 | 说明 | 默认值 |
---|---|---|
[[Configurable]] | 能否通过delete删除属性从而重新定义属性,能否修改属性的特性,能否把属性修改为访问器属性 | true |
[[Enumerable]] | 能否通过for-in循环返回属性 | true |
[[Writable]] | 能否修改属性的值 | true |
[[Value]] | 包含这个属性的数据值,读属性时,从这个位置读;写属性时,从这个位置写 | undefined |
Object.defineProperty(对象名,"属性名",{ //若对象中没有该属性,则会创建该属性
//以下四个不用全写,注意开头为小写
configurable : true或false,
enumerable : true或false,
writable : true或false,
value : 值
});
注意:
访问器属性不包含数据值,它们包含一对getter和setter函数(这两个函数不是必须的),在读取访问器属性时,会调用getter函数,在写入访问器属性时,会调用setter函数
访问器特性 | 描述 | 默认值 |
---|---|---|
[[Configurable]] | 能否通过delete删除属性从而重新定义属性,能否修改属性的特性,能否把属性修改为数据属性 | true |
[[Enumerable]] | 能否通过for-in循环返回属性 | true |
[[Get]] | 读取属性时调用的函数 | undefined |
[[Set]] | 写入属性时调用的函数 | undefined |
var object_name = {
_attribute : 属性值 //以开头加 _ 表示只能通过对象方法访问的属性,语法习惯,不强求
};
Object.defineProperty(object_name,"attribute",{ //此处attribute前没有_ ,attribute是
//以下四个不用全写,注意开头为小写 _attribute的访问器属性
configurable : true或false,
enumerable : true或false,
get : function(){
……
return this._attribute;
},
set : function(newValue){
……
this._attribute = newValue;
}
});
object_name.attribute = 新属性值; //此处后台调用了set方法
var a = object_name.attribute; //此处后台调用了get方法
Object.defineProperties()方法
Object.defineProperties(对象名,{
属性1:{
特性1:特性值1,
特性2:特性值2,
…………
},
属性1:{
特性1:特性值1,
特性2:特性值2,
…………
},
…………
});
Object.getOwnPropertyDescriptor(对象名,"属性名");
返回值是一个对象
如果是访问器属性,这个对象的属性有configurable、enumerable、get、set
如果是数据属性,这个对象的属性有configurable、enumerable、writable、value
用函数来封装创建对象的细节
function 工厂函数名 (参数列){
var o = new Object();
o.属性1 = 参数1;
o.属性2 = 参数2;
…………
o.方法1 = function (参数列){
函数体; //此处使用o的属性应当写this.属性名
}
}
function 构造函数名(参数列){ //JavaScript没有类,构造函数名相当于类名
this.属性1 = 参数1;
this.属性2 = 参数2;
…………
this.方法1 = function (参数列){
函数体;
}
}
var 变量名 = new 类名(参数列);
要使用构造函数创建实例,必须使用new操作符,具体过程如下:
用构造函数创建的对象既是Object的实例,也是构造方法名字的实例
构造函数和普通函数唯一的区别就是调用方式不同,构造函数必须用new操作符调用
构造函数的问题在于:每个方法要在每个实例上重建一遍
解决办法:
1.把函数定义转移到构造函数外部
function 构造函数名(参数列){
…………
this.方法1 = 函数名1;
}
function 函数名1(参数列){
函数体:
}
2.原型模式
我们创建的每个函数都有一个prototype属性,该属性是一个指针,指向一个对象,这个对象的用途是包含可以由特定类型的所有实例共享的属性和方法
不必在构造函数中定义对象实例的信息,而是可以将这些信息直接添加到原型对象中
function 构造函数名(){}
构造函数名.prototype.属性名1 = 属性值1;
构造函数名.prototype.属性名1 = 属性值1;
…………
构造函数名.prototype.方法名1 = function(参数列){
函数体:
}
原型模式与构造函数模式最大的区别是新对象的属性和方法是所有实例共享的,不能通过实例修改,只能修改原型对象,为共享的属性赋值将会创建一个新的实例属性覆盖原型对象中的属性
简化的原型语法(对象字面量形式)
function 构造函数名(){}
构造函数名.prototype = { //创建新对象,重写prototype属性,相当于prototype = new Object()
属性名1 : 属性值1, //多个键值对用逗号隔开,最后一个不用加逗号
属性名2 : 属性值2,
…………
方法名1 : function(参数列){
函数体:
},
…………
}
//此处以后才可以创建实例
注意:
对象字面量形式本质为创建新对象,该构造函数的prototype属性被重写
因此必须在构造函数名.prototype = {……} 语句后才能创建实例
以这种方式创建实例,原型对象的constructor属性不再指向构造函数,尽管instanceof操作符仍能返回正确的结果,但通过constructor属性以及无法确定对象类型了
如果constructor属性很重要,可以在设置原型对象时手动设置constructor属性
构造函数名.prototype = {
constructor : 构造函数名,
…………
}
设有构造函数 function A ( ){……} var a = new A(); 则:
每当代码读取某个对象的某个属性时,都会执行一次搜索,目标是具有给定名字的属性,搜索首先从对象实例本身开始,若找到则返回该属性的值,如果没找到,则继续搜索指针指向的原型对象
虽然可以通过对象实例访问保存在原型中的值,却不能通过对象实例重写原型中的值,如果我们在实例中添加了一个属性,该属性与原型中一个属性同名,则该属性会屏蔽掉原型中的那个属性
使用delete删除掉实例属性后,就能重新访问原型中的属性了
使用hasOwnProperty()方法可以检测一个属性存在于实例还是存在于原型中,当给定属性存在于对象实例时,返回true,该函数是从Object中继承来的
实例.hasOwnProperty("属性名");
有两种方法使用in操作符:单独使用、在for-in循环中使用
直接使用
"属性名" in 对象名 //通过对象能够直接访问给定属性时返回true,无论该属性在实例中还是在原型中
使用hasOwnProperty()和in操作符就可以确定该属性到底存在于对象中,还是存在于原型中
//若属性存在于原型中,返回true;若属性存在于实例中,返回false
function hasPrototypeProperty(object,attribute_name){
return !object.hasOwnProperty(attribute_name) && (attribute_name in object);
}
for-in
属性的遍历
我们对原型对象所做的任何修改都能够立即从实例上反映出来,即使先创建了实例再修改也是如此
绝对不能重写原型对象
他省略了为构造函数传递参数之一环节,结果所有实例在默认情况下都能取得相同的属性值
若原型对象包含引用类型值,则会导致多个实例操作同一个引用对象,这是不合理的,每个实例应当拥有自己独立的属性值
创建自定义类型的最常见方式,就是组合使用构造函数模式与原型模式
构造函数模式用于定义实例属性,原型模式用于定义方法和共享的属性
function object_name(参数列){
this.属性名1 = 属性值1;
this.属性名2 = 属性值2;
…………
}
object_name.prototype = {
constructor : object_name,
共享属性名 : 共享属性值,
…………
方法名 : function(参数列){
函数体;
},
…………
}
var 实例名 = new object_name(参数列);
在构造函数中初始化原型,既书写简便,又保持了组合使用构造函数模式与原型模式的优点
function object_name(参数列){
this.属性名1 = 属性值1;
this.属性名2 = 属性值2;
…………
if(typeof this.方法1 != "方法1"){ //仅在第一次调用构造函数时修改原型对象
object_name.prototype.共享属性名 = 共享属性值; //不能使用字面量写法,字面量写法相当于重写 prototype,将会切断实例与新原型之间的连接
…………
object_name.prototype.方法 = function(参数列){
函数体;
}
}
}
function 构造函数名 (参数列){
var o = new Object();
o.属性1 = 参数1;
o.属性2 = 参数2;
…………
o.方法1 = function (参数列){
函数体; //此处使用o的属性应当写this.属性名
}
return o;
}
var 实例名 = new 构造函数名(参数列);
除了使用new操作符并把包装函数叫做构造函数之外,这个模式其实和工厂函数一模一样
通过在构造函数末尾添加 return 语句,可以重写调用构造函数时返回的值
用于想要修改某类型构造函数,却又不能修改时使用
返回的对象与构造函数或构造函数的原型之间没有关系
instanceof操作符无法确定其实例的类型
在能够使用其他模式的情况下,不要用这种模式
function object_name(参数1,参数2,……){
var o = new Object();
//此处定义私有变量和方法
o.getparam1 = function(){
return 参数1; //此处的参数1并不是一个属性,它只能通过该方法访问
}
return o;
}
var 实例名 = object_name("2233",……);
实例名.getparam1(); //返回“2233”
由于函数没有签名,在JavaScript中无法实现接口继承,只支持实现继承,其实现继承主要是依靠原型链实现的
利用原型让一个引用类型继承另一个引用类型的属性和方法
原理:让原型对象等于另一个类型的实例
function SuperType(){ //父类构造函数
//定义父类的属性
this.属性名 = 属性值;
…………
}
SuperType.prototype.方法 = function(……){……} //定义父类的方法
…………
function SubType(){ //子类构造函数
//定义子类的属性
this.属性名 = 属性值;
…………
}
SubType.prototype = new SuperType(); //将子类的原型指定为父类的一个实例,该实例拥有父类的全部属性和方 法,从而实现继承,必须在该行之后才能定义子类方法
SubType.prototype.方法 = function(……){……} //定义子类的方法
因此实际工作中很少用到纯原型链
function SubType(){
SuperType.call(this,参数列); //使用此种方式创建的子类对象本质上是父类对象实例
}
var 实例名 = new SubType();
将原型链和借用构造函数技术组合到一起
//定义父类
function SuperType(参数列){
this.父类属性 = 属性值;
…………
}
SuperType.prototype.父类方法 = function(……){……}
//定义子类
function SubType(参数列){
SuperType.call(this,参数列);
this.子类属性 = 属性值;
…………
}
//继承方法
SubType.prototype = new SuperType();
SubType.prototype.constructor = SubType;
//定义子类方法
SubType.prototype.子类方法 = function(……){……}
var 实例名 = new SubType(属性列);
借助原型可以基于已有的对象创建新对象,同时还不必因此创建自定义类型
function object(o){ //o为已有对象,作为新对象的原型
function F(){}
F.prototype = o;
return new F();
}
Object.create()方法亦可实现上述功能
var 实例名 = Object.create(作为新对象原型的对象,{
属性名1 : { //此处的写法与设置属性特性相同
value : 属性值,
…………
}
属性名2 : {设置特性}
…………
})
创建一个仅用于封装继承过程的函数
function create(original){ //original为对象原型
var clone = object(original); //此处object()为原型式继承的方法
clone.属性名 = 属性值; //以某种方式增强这个对象
clone.方法名 = function(……){……} //此处定义的函数无法实现复用
return clone; //返回这个对象
}
任何能返回新对象的函数都适用于此模式
使用寄生式继承为对象添加函数,会因为无法实现函数复用而降低效率
通过借用构造函数来继承属性,通过原型链的混成形式来继承方法
function inheritPrototype(subType,superType){
var prototype = object(superType.prototype); //此处object()为原型式继承的方法
prototype.constructor = subType;
subType.prototype = prototype;
}
//定义父类
function SuperType(参数列){
this.父类属性 = 属性值;
…………
}
SuperType.prototype.父类方法 = function(……){……}
//定义子类
function SubType(参数列){
SuperType.call(this,参数列);
this.子类属性 = 属性值;
…………
}
//子类继承父类
inheritPrototype(subType,superType);
//定义子类方法
SubType.prototype.子类方法 = function(……){……}
var 实例名 = new SubType(属性列);