对象。每个对象都有一个内部链接到另一个对象,称为它的原型 prototype。该原型对象有自己的原型,等等,直到达到一个以null为原型的对象。根据定义,null没有原型,并且作为这个原型链 prototype chain中的最终链接。
属性类型
- 数据属性
// 用于修改属性的描述符
var person = {};
Object.defineProperty(person,"name",{
Configurable:false,// 允许通过delete删除属性,重新定义属性?默认true
Enumerable:false,// 允许枚举?默认true
writable:false,// 允许修改值?默认true
Value:"nicholas",// 值默认 undefined
});
person.name;//nicholas
person.name = "greg";
person.name;//nicholas
var person = {};
Object.defineProperty(person,"name",{
Configurable:false,
Value:"nicholas",// 值默认 undefined
});
// 抛出错误
Object.defineProperty(person,"name",{
Configurable:true,
Value:"nicholas",// 值默认 undefined
});
- 访问器属性
Configurable:false,// 允许通过delete删除属性,重新定义属性?默认true
Enumerable:false,// 允许枚举?默认true
get,// 读 类型function 值默认 undefined
set,// 写 类型function 值默认 undefined
var book = {
_year:2004,
edition:1
};
Object.defineProperty(book,"year",{
get: function(){
return this._year;
},
set: function(newValue){
this._year = newValue;
this.edition += 1;
}
});
book.year = 2005;
book.edition;//1
// 属性前的 _ 是一个记号,表示只能通过对象方法访问的属性
// 仅制定get,属性不可写,尝试写会被忽略
// defineProperty 出现之前,是这样写的如下:
var book = {
_year:2004,
edition:1
};
book.__defineGetter__("year",function(){
return this._year;
});
book.__defineSetter__("year",function(newValue){
this._year = newValue;
this.edition += 1;
}
});
book.year = 2005;
book.edition;//1
定义多个属性
var book = {};
Object.defineProperty(book,{
_year:{
value:2004
},
edition:{
value:1
},
year:{
get: function(){
return this._year;
},
set: function(newValue){
this._year = newValue;
this.edition+=1;
}
}
});
//_year, edition 两个数据属性,数据属性
// year 一个访问属性(访问器),
// 读取属性的特性
var descriptor = Object.getOwnPropertyDescriptor(book,"_year");
descriptor.value;// 2004
descriptor.configurable;// false
typeof descriptor.get;// undefined
// 访问属性的特性
var descriptor = Object.getOwnPropertyDescriptor(book,"year");
descriptor.value;// undefined
descriptor.configurable;// false
typeof descriptor.get;// function
创建对象
- 工厂模式
function createdPerson(name,age){
var person = new Object();
person.name = name;
persion.age = age;
return person;
}
var person1 = createdPerson("xiaoming",29);
var person2 = createdPerson("xiaohong",29);
- 构造函数
// 上面代码改良如下
function Person(name, age){
this.name = name;
this.age = age;
}
var person1 = new Person("xiaoming",29);
var person2 = new Person("xiaohong",29);
// 通过构造函数创建的实例,constructor属性保存指向Person
person1. constructor;//Person
person1 instance Object;//true
person1 instance Person;//true
// 通上,所有对象继承自Object
// 构造函数的缺点
function Person(name, age){
this.name = name;
this.age = age;
this.say = function (){
alert(this.name);
}
}
var person1 = new Person("xiaoming",29);
var person2 = new Person("xiaohong",29);
// 这样person1和person2会存在,两个相同功能的function对象,造成资源的浪费
// 改进
function Person(name, age){
this.name = name;
this.age = age;
this.say = say;
}
function say(){
alert(this.name);
}
原型对象
// 上面的解决方案,导致新的问题:在全局作用域的函数缺只能被某个对象是用,且若很多方法的话
// 需定义很多的全局函数。则毫无封装。。
// 原型模式:(原理,基于函数的prototype属性,此属性指向函数的原型对象)
//上面代码改称为
function Persion(){
}
Persion.prototype.name = "xiaoming";
Persion.prototype.age = 29;
Persion.prototype.say = function (){
alert(this.name);
};
var person1 = new Person();
person1.say();//xiaoming
// 这些属性是所有实例共享的
Person,Persion.prototype,person1,person2关系如下:
对象,原型,实例关系的参考
注意:当我们创建一个函数,系统就会为这个函数自动分配一个prototype指针,指向它的原型对象。并且可以发现,这个原型对象包含两个部分(constructor 和 proto)其中constructor指向函数自身。(这里形成了一个小闭环) 当我们将该函数作为模版创建实例(new方法)的时候,我们发现创建出的实例是一个与构造函数同名的object,这个object是独立的,他只包含了一个 proto指针(实例没有prototype,强行访问则会输出undefined),这个指针指向上面提到的构造函数的prototype原型对象。 这时候我们发现三者形成了一个大"闭环"。之所以加上引号,因为构造函数和实例之间无法直接访问,需要通过 proto指针间接读取。
prototype:此属性只有函数有
constructor:函数的原型对象的属性,指向函数本身
prototype:函数的属性,指向函数的原型对象
proto:实例的属性,指向函数的原型对象,构造函数和实例之间无法直接访问。
person1.__proto__ == Person.prototype;// true
person1.__proto__.constructor == People;//true
// 查找对象是否存在原型关系
Person.prototype.isPrototypeof(person1);// true
Person.prototype.isPrototypeof(person2);// true
// 获取Prototype
Object.getPrototypeof(person1) == Person.prototype;//true
Object.getPrototypeof(person1).name;//xiaoming
这种情况下,person1.name;实际是查询的原型链上的name属性,若查找.一个不存在的属性,那么会遍历原型链所有的属性,很浪费性能
// 原型链
function Persion(){
}
Persion.prototype.name = "xiaoming";
Persion.prototype.age = 29;
Persion.prototype.say = function (){
alert(this.name);
};
var person1 = new Person();
person1.say();//xiaoming 来自原型
var person2 = new Person();
person2.name = "xiaohong";
person2.say();//xiaohong 来自实例
// 原型链的查找,先查自身的属性,查不到依次往上查原型链,
// delete 操作符,删除实例的属性
delete person2.name;
person2.say();//xiaoming 来自原型
// hasOwnProperty(); 检测一个属性存在实例还是原型,参数是属性名
// 原理见下图
// in操作符:无论存在于原型还是实例,存在即返回true
var person1 = new Person();
var person2 = new Person();
person1.hasOwnProperty("name");//false
"name" in person1;//true
person1.name = "xiaoming";
person1.hasOwnProperty("name");//true
"name" in person1;//true
// 应用
// 判断是原型属性
function hasPrototypeProperty(object , name){
return ! object.hasOwnProperty(name)&&(name in object);
}
//for in 可以获取对象所有可枚举的属性[enumerable],还可以使用Object.keys()方法
function Person(){
}
Person.prototype.name = "xiaoming";
Person.prototype.age = 29;
Person.prototype.say = function (){
alert(this.name);
}
// 获取实例属性
var keys = Object.keys(Person.prototype);// name,age,say
var p1 = new Person();
p1.name = "11";
p1.age = 22;
var keys = Object.keys(p1);// name age
// 获取所有属性无论是否可以枚举
var keys = Object.getOwnPropertyNames(Person.prototype);// constructor,name,age,say
// 更简单的原型语法
// 此方式会导致enumerable属性被标记为true,原声的enumerable默认事false不可枚举
function Person(){
}
// 重写原型
Person.prototype = {
constructor:Person,//将其设为Person保证通过该属性获取适当值
name:"nike",
age:29,
say:function (){
alert(this.name);
}
};
// 结合defineproperty函数
//最上面有defineProperty函数的讲解,第一个参数实例,第二个参数属性名,第三个参数设置
// 这是数据属性的设置
Object.defineProperty(Person.property,"constructor",{
enumerable:false,
value:Person
});
}
// 注意一下俩例子,如图,实例的原型,是一个对象,有自己的属性
// 动态原型模式(懒加载)
function Person(name ,age){
this.name = name;
this.age = age;
if(typed this.sayname != "function"){
Person.prototype.sayname = function (){
alert(this.name);
}
}
}
// 装饰模式
// 创建一个具有额外方法的特殊数组
function SpecialArray(){
var values = new Array();
// 数组的push属性(类型function),调用apply函数,添加值
values.push.apple(values,arguments);//values :当前作用域 arguments :参数列表
// 添加方法
values.toPipedString = function(){
return this.join("|");
}
return values;
}
var colors = new SpecialArray("red","blue","green");
colors.toPipedString();//red|blue|green
// 稳妥构造,创建私有属性
function Person (name,age,job){
var o = new Object();
o.sayName = function(){
alert(name);
}
return o;
}
// 这种情况下,只有sayName才能访问name
var friend = new Person("xiaoming",29,"ht");
friend.sayName();//xiaoming
原型链
function SuperType(){
this.property = true;
}
SuperType.prototype.getSuperValue = function (){
return this.property;
}
function SubType(){
this.subproperty = false;
}
SubType.prototype = new SuperType();
SubType.prototype.getSubValue = function (){
return this.subproperty;
}
// 测试
var instance = new SubType();
instance.getSuperValue();// true
// 原理见下图
// 完整的原型链
// 确定原型和实例的关系
// 添加原型方法的代码一定放在替换原型之后
// 继承
function SuperType(name){
this.name = name;
}
function SubType(){
SuperType.call(this,"Nicholas");
this.age = 23;
}
var instance = new SubType();
instance.name; // Nicholas
instance.age; // 23
// 组合继承
function SuperType(name){
this.name = name;
this.colors = ["red","blue","green"];
}
SuperType.prototype.sayName = function(){
alert(this.name)
}
function SubType(name,age){
SuperType.call(this,name);
this.age = age;
}
// 继承方法
SubType.prototype = new SuperType();
SubType.prototype.sayAge = function(){
alert(this.age);
}
var instance1 = new SubType("Nicholas",29);
instance1.colors.push("black");//red,blue,green,black
instance1.sayAge;//Nicholas
instance1.sayName;//29
var instance2 = new SubType("Grey",27);
instance2.colors;//red,blue,green
instance2.sayAge;//Grey
instance2.sayName;//27
// 原型式继承
//eg
function object(o){
function F(){ }
F.prototype = o;
return new F();
}
var person = {
name:"nick",
friends:["shelby","court"];
};
var anotherObj = object(person);
anotherObj.name = "greg";
anotherObj.friends.push("rob");
var yeObj = object(person);
yeObj.name = "linda";
yeObj.friends.push("bar");
person.friend;//"shelby","court", rob, bar
// 上面的思想 在 js 有个方法规范了
// 重写为
var person = {
name:"nick",
friends:["shelby","court"];
};
var anotherObj = Object.create(person);
anotherObj.name = "greg";
anotherObj.friends.push("rob");
var yeObj = Object.create(person);
yeObj.name = "linda";
yeObj.friends.push("bar");
person.friend;//"shelby","court", rob, bar
// create 有两个参数,第二个参数与Object.defineProperties()方法的第二个参数作用一致,覆盖原型上的同名属性
// eg
var person = {
name:"nick",
friends:["shelby","court"];
};
var anotherObj = Object.create(person,{
name:{
value:"greg"
}
});
anotherObj.name;//greg
// 寄生式继承(和原型式紧密)
function object(o){
function F(){ }
F.prototype = o;
return new F();
}
function createrAnother(o){
var clone = object(o);
clone.sayHi = function (){
alert("hi");
}
return clone;
}
// 使用
var person = {
name:"nick",
friends:["shelby","court"];
};
var anotherPerson = createrAnother(person);
anotherPerson.sayHi();// hi
// 寄生组合式
// 先看一个组合继承的例子
function SuperType(name){
this.name = name;
this.colors = ["red","green"];
}
SuperType.protoType.sayName = function (){
alert(this.name);
}
function SubType(name,age){
SuperType.Call(this,name);//第一次
this.age = age;
}
SubType.prototype = new SuperType();//第二次
SubType.prototype.constructor = SubType;
SubType.prototype.sayAge = function (){
alert(this.age);
}
// 注意了 SuperType() 一共调用了两次
// 为解决调用了两次 SuperType() ,寄生组合应运而生
//上面代码的这一部分
SubType.prototype = new SuperType();//第二次
SubType.prototype.constructor = SubType;
// 可以替换为
function inheritPrototype(SubType,SuperType){
var prototype = object(SubType, SuperType);// 创建
prototype.constructor = SubType;// 增强
SubType.prototype = prototype;// 制定
}
inheritPrototype(SubType, SuperType);//用这句话代替
// 替换的图示如下: