-
理解对象
-
创建对象
-
工厂模式
- 创建示例
function createCompany(name,age,job){ var o = new Object(); o.name = name; o.age = age; o.job = job; o.getName = function(){ alert(this.name); } return o; } var company1 = createCompany('yiyi',25,'IT'); var company2 = createCompany('xin',26,'doctor');
- 问题
- 可创建多个相似对象,但是却无法识别对象类型。
- 创建示例
-
构造函数模式
- 创建示例
function CreateCompany(name,age,job){ this.name = name; this.age = age; this.job = job; this.getName = function(){ alert(this.name); } } var company1 = new CreateCompany('yiyi',25,'IT'); var company2 = new CreateCompany('xin',26,'doctor');
- 理解
- 调用构造器函数的过程
- 创建一个新对象
- 将构造函数的作用域赋给新对象(this就指向了这个新对象);
- 执行构造函数中的代码(为新对象添加属性);
- 返回新对象
- 通过构造器函数模式创建的对象,既是Object的实例,同时也是该构造器函数的实例。
company1 instanceOf Object;//true company1 instanceOf CreateCompany;//true company2 instanceOf Object;//true company2 instanceOf CreateCompany;//true
- 调用构造器函数的过程
- 注意
- 要创建构造函数的实例,必须使用new操作符。
- 使用构造函数,每个方法都要在每个实例上重新创建一遍。
- 不同实例上的同名函数是不相等的。
alert(company1.getName == company2.getName); //false
- 要实现同名函数相同,我们可以把同名函数设置成全局函数,但是函数增多时,不利于实现封装。
function CreateCompany(name,age,job){ this.name = name; this.age = age; this.job = job; this.getName = getName; } //设置成全局函数 function getName (){ alert(this.name); } var company1 = new CreateCompany('yiyi',25,'IT'); var company2 = new CreateCompany('xin',26,'doctor');
- 创建示例
-
原型模式
- 原型模式示例
function Person(){}; Person.prototype.name = 'yiyi'; Person.prototype.age = 26; Person.prototype.sayName = function(){ alert(this.name); }; var person1 = new Person(); var person2 = new Person(); alert(person1.name);//yiyi alert(person2.name);//yiyi alert(person1.sayName == person2.sayName ); //true alert(Person.prototype.isPrototypeOf(person1));//true
- 使用原型对象的好处
- 让所有对象实例共享它所包含的属性和方法。
- 原型模式特点
- 可以通过对象实例访问原型对象中的值,但是却不能通过重写实例中的值去改变原型对象中的值。
person1.name = 'qianxi'; alert(person1.name);//qianxi 来自实例对象 alert(person2.name);//yiyi 来自原型对象
-
对象实例中存在与原型对象同名属性,则会优先访问对象实例中的属性,断开与原型的连接,只有通过delete删除该属性时,才会重新与原型连接,访问原型中的该同名属性。
person1.name = 'yixi'; alert(person1.name);//yixi 来自对象实例 person1.name = null; alert(person1.name);//null 来自对象实例 alert(person1.hasOwnProperty('name'));//true delete person1.name; alert(person1.name);//yiyi 来自原型对象 alert(person1.hasOwnProperty('name'));//false
-
引用类型值的属性,会在每个实例中实现共享,改变一个实例的该属性值则会更改所有实例的该属性值。
- 可以通过对象实例访问原型对象中的值,但是却不能通过重写实例中的值去改变原型对象中的值。
- 理解原型对象
- 只要创建一个函数,那么它就会拥有一个prototype属性,并且该属性指向该函数的原型对象。
- 每个原型对象都会有一个constructor(构造函数)属性,该属性包含一个指向prototype属性所在函数的指针。即函数原型对象的constructor属性指向该函数。Person.prototype.constructor = Person。
- 调用构造函数创建实例后,该实例会有一个指针【__proto__】,指向该构造函数的原型对象。
- 想要确定实例和构造函数的原型对象的关系,可以通过getPrototypeOf()来确定。Person.prototype.isPrototypeOf(person1);
- ECMAScript5新增一个方法:Object.getPrototypeOf() 获取对象的原型,这个方法对于利用原型实现继承非常重要。
- hasOwnProperty():判断属性是否属于对象实例。返回false说明属性来自原型。
- in操作符能确定该属性是否存在于对象实例或者原型中。配合hasOwnProperty()能确定属性是否存在于对象实例或者原型中。
function hasPrototypeProperty(object, proName){ return !hasOwnProperty(proName) && (proName in object); }
- 获取对象的属性
- for - in
- 示例
function Person(){}; Person.prototype.name = 'yiyi'; Person.prototype.age = 26; Person.prototype.sayName = function(){ console(this.name); }; var person1 = new Person(); var person2 = new Person(); for(var prop in person1){ console.log(prop,'====prop ='); } // name ====prop = // age ====prop = // sayName ====prop = for(var props in Person.prototype){ console.log(props,'=====Person type prop=='); } // name =====Person type prop== // age =====Person type prop== // sayName =====Person type prop== person1.name = 'xin'; person1.friends = 'jajka'; for(var prop in person1){ console.log(prop,'====prop change ='); } //name ====prop change = //friends ====prop change = //age ====prop change = //sayName ====prop change = for(var prop in person2){ console.log(prop,'====porp 2='); } //name ====porp 2= //age ====porp 2= //sayName ====porp 2= console.log(person1.name); //xin console.log(person2.name); //yiyi
- for - in既包含实例中的属性,也包括原型中的属性(但均为能通过对象访问的、可枚举属性)。
- 示例
- Object.keys():接受一个对象作为参数,返回一个包含所有可枚举属性的字符串数组。均为对象自有可枚举的属性。
- 示例
function Person(){}; Person.prototype.name = 'yiyi'; Person.prototype.age = 26; Person.prototype.sayName = function(){ console(this.name); }; var person1 = new Person(); var person2 = new Person(); var keys1 = Object.keys(Person.prototype); console.log(keys1); //['name','age','sayName'] var keys2 = Object.keys(person1); console.log(keys2); //[] person1.name = 'xin'; person1.friends = 'jajka'; var keys3 = Object.keys(person1); console.log(keys3); //['name','friends'] var keys4 = Object.keys(person2); console.log(keys4);//[] console.log(person1.name); //xin console.log(person2.name); //yiyi
- 注意
- 内置属性一般为不可枚举属性,当重新创建覆盖原来的属性时,该属性就变成了可枚举。
//原型的属性对实例而言,默认是不可枚举的属性 person1.name = 'xin'; person1.friends = 'jajka'; var keys3 = Object.keys(person1); console.log(keys3); //['name','friends']
- 内置属性一般为不可枚举属性,当重新创建覆盖原来的属性时,该属性就变成了可枚举。
- 示例
- Object.getOwnPropertyNames():获取对象自有的所有属性,包括可枚举和不可枚举。
- 示例
function Person(){}; Person.prototype.name = 'yiyi'; Person.prototype.age = 26; Person.prototype.sayName = function(){ console(this.name); }; var person1 = new Person(); var person2 = new Person(); //默认情况下,原生的constructor属性是不可枚举的 var keys1 = Object.keys(Person.prototype); console.log(keys1); //["constructor", "name", "age", "sayName"] var keys2 = Object.getOwnPropertyNames(person1); console.log(keys2);//[] person1.name = 'xin'; person1.friends = 'jajka'; var keys3 = Object.getOwnPropertyNames(person1); console.log(keys3); //['name','friends'] var keys4 = Object.getOwnPropertyNames(person2); console.log(keys4);//[] //定义一个不可枚举对象 Object.defineProperty(person1, "age", {value:"18", enumerable:false}); var keys5 = Object.getOwnPropertyNames(person1); console.log(keys5); //['name','friends','age']
- 示例
- for - in
- 原型模式示例
-
组合使用构造函数模式和原型模式
- 创建示例
function Person(name,age,job){ this.name = name; this.age = age; this.job = job; this.colors = ['red','green','blue']; } Person.prototype = { constructor: Person, getName: function(){ alert(this.name); } } var person1 = new Person('yiyi',25,'IT'); var person2 = new Person('xin',26,'doctor'); person1.colors.push('pink'); alert(person1.colors);//['red','green','blue','pink']; alert(person2.colors);//['red','green','blue'] alert(person1.colors == person2.colors); //false alert(person1.getName == person2.getName); //true
- 理解
- 实例属性是在构造函数中定义的,儿所有实例共享的属性constructor和方法则是在原型中定义的。
- 创建示例
-
继承
-
原型链继承
- 示例
function Parent(){ this.name= 'yiyi'; this.age = 12; } Parent.prototype.getParentName = function(){ return this.name; } function Child(){ this.firstName = 'yixi'; } //继承了Parent Child.prototype = new Parent(); Child.prototype.getChildName = function(){ return this.firstName; } var child1 = new Child(); console.log(child1.getParentName()); //yiyi console.log(child1.getChildName ()); //yixi console.log(child1);
child1的打印结果如下:
- 注意
- 通过原型链进行继承的时候,不能通过对象字面量去创建原型的方法,这样会重写原型链,断开原有原型链的连接。
function Parent(){ this.name= 'yiyi; } Parent.prototype.getParentName = function(){ return this.name; } function Child(){ this.firstName = 'yixi'; } //继承了Parent Child.protoype = new Parent(); //使用字面量增加新方法,会导致上面的代码失效 Child.prototype = { getChildName: function(){ return this.firstName; }, getOtherName: function(){ return 'other'; } } var child1 = new Child(); alert(child1.getParentName()); //报错
- 通过原型链进行继承的时候,不能通过对象字面量去创建原型的方法,这样会重写原型链,断开原有原型链的连接。
- 原型链的问题
- 引用类型值的原型属性,会被所有的实例共享。(改变一个引用类型值的原型属性的值,会更改所有实例)
- 创建子类型的实例时,不能想超类型的构造函数传参。
- 示例
-
构造函数继承
- 使用示例
function Parent(name){ this.name= name; this.colors = ['blue','green','red']; this.getName = function(){ console.log(this.name); } } Parent.prototype.getColors = function(){ console.log(this.colors); } function Child(){ //继承了Parent Parent.call(this,'yiyi'); this.age = 25; } var person1 = new Child(); person1.colors.push('pink'); console.log(person1.name); //yiyi console.log(person1.age); //25 console.log(person1.colors);//['blue','green','red','pink'] var person2 = new Child(); console.log(person2.colors);//['blue','green','red'] console.log(person1); //Child {name: "yiyi", colors: Array(4), getName: ƒ(), age: 25}
- 问题
- 方法都在构造函数中定义,不符合函数复用。
- 在超类型的原型中定义的方法,对子类型而言是不可见的。如上查看实例,没有超类型原型中定义的getColors方法。
- 使用示例
-
组合继承
- 思路:使用原型链实现对原型属性和方法的继承,而通过借用构造函数来实现对实例属性的继承。
- 使用示例
function Parent(name){ this.name= name; this.colors = ['blue','green','red']; } Parent.prototype.getName = function(){ alert(this.name); } function Child(name,age){ //继承了属性 Parent.call(this,name); this.age = age; } //继承方法 Child.prototype = new Parent(); Child.prototype.getAge = function(){ alert(this.age); } var person1 = new Child('yiyi',25); person1.colors.push('pink'); alert(person1.getName()); //yiyi alert(person1.getAge()); //25 alert(person1.colors);//['blue','green','red','pink'] var person2 = new Child('xinxin',18); alert(person2.getName()); //xinxin alert(person2.getAge()); //18 alert(person2.colors);//['blue','green','red']
-
原型式继承
- 创建示例
function object(o){ function F(){}; F.prototype = o; return new F(); } var person = {name: 'yiyi',colors:['green','red','blue']}; var person1 = object(person); person1.name = 'yixi1'; //yixi1 person1.colors.push('pink'); var person2 = object(person); person2.name = 'yixi2'; //yixi2 person2.colors.push('yellow'); console.log(person1.colors);//['green','red','blue','pink','yellow']; console.log(person2.colors);//['green','red','blue','pink','yellow']; console.log(person.colors); //['green','red','blue','pink','yellow'];
- 原型式继承解析
- 在object()函数内部,先创建了一个临时性的构造函数,然后将传入的对象作为这个构造函数的原型,最后返回了这个临时类型的一个新实例。
- object()对传入其中的对象执行了一次浅复制。
- Object.create()
- ECMAScript5新增了Object.create()方法规范化了原型式继承。Object.create()与object()行为相同。
- 适用场景
- 只想让一个对象与另一个对象保持一致。
- 注意
- 包含引用类型值的属性始终都会共享相应的值,就像使用原型模式一样
- 创建示例
-