对象
什么是对象
ECMA-262把对象定义为:“无序属性的集合,其属性可以包含基本值、对象或者函数”
- 属性的类型
访问器属性:[[Configurable]],[[Enumerable]],[[Writeable]],[[Value]],访问器属性:[[Configurable]],[[Enumerable]],[[Get]],[[Set]].如果要修改以上属性的特性,需要调用Object.defineProperty()
,其参数为属性所在的对象、属性的名字、一个描述符对象。定义多个属性Object.defineProperties(要修改的对象,修改后的对象)
,读取这些特性:Object.getOwnPropertyDescriptor()
- 属性的类型
创建对象
工厂模式(通过函数封装以特定接口创建对象的细节)
function createPerson (name, age, job) { let o = new Object(); o.name = name; o.age = age; o.job = job; o.sayName = function(){ alert(this.name) }; return o; } let person = createPerson('hy', '20', 'software Engineer') // 没有解决对象识别问题(即怎样知道一个对象的类型)
构造函数模式(自定义构造函数)
function Person (name, age, job) { this.name = name; this.age = age; this.job = job; this.sayName = function(){ alert(this.name) }; } let person = createPerson('hy', '20', 'software Engineer') // 没有显示创建对象、将属性和方法赋给了this、没有return
缺点:不同实例上的同名函数是不相等的,即每个实例都包含一个不相同的Function实例
- 原型模式(将属性和方法添加到原型上,所有的实例共享属性和方法)
面试题:prototype 和 proto 有什么区别?
prototype 是函数的,指向函数的原型对象,创建一个实例后,实例内部有个[[prototype]],脚本里无法访问,但浏览器有一个proto指向构造函数的原型对象(即当函数为构造函数是,prototype和proto指向同一个原型对象)
可以通过hasOwnProperty()来检测属性存在于哪里,只有当属性存在于实例上,才会返回true in操作符,只要能访问到属性就返回true,不论是原型上还是实例上。 for-in 循环返回能通过对象访问的所有可枚举的属性,要访问实例上所有可枚举属性,使用Object.keys()function Person = { } Person.prototype.name = 'hy'; Person.prototype.age = 20; Person.prototype.job = 'software Enginner' Person.prototype.sayName = function(){ alert(this.name) } // 更简单的原型语法
- 组合使用构造函数模式和原型模式(构造函数模式用于定义实例属性,原型模式定义方法和共享属性)
function Person(name, age, job) { this.name = name; this.age = age; this.job = job; this.friends = ['yh', 'hy'] } Person.prototype = { constructor: Person, sayName: function (){ alert(this.name) } }
instanceof 运算符用来测试一个对象在其原型链中是否存在一个构造函数的 prototype 属性。
typeof 一般用来测试基本类型继承对象
- 原型链
每个实例对象(object )都有一个私有属性(称之为 proto)指向它的原型对象(prototype)。该原型对象也有一个自己的原型对象 ,层层向上直到一个对象的原型对象为 null。根据定义,null 没有原型,并作为这个原型链中的最后一个环节。 - 基于原型链实现继承(基本模式)
function SuperType(){ this.property = true; } SuperType.prototrpt.getSuperValue = function(){ return this.property; }; // 组合使用构造函数和原型模式 function SubType(){ this.subproperty = false; } SubType.prototype = new SuperType(); // !important SubType.prototype.getSubValue = function(){ return this.subproperty; }; var instance = newSubType(); // 让原型对象等于父级的实例
存在的问题:不能向超类型的构造函数中传递参数,引用类型的问题
- 借用构造函数
function SuperType(name){ this.name = name; } function SubType(){ SuperType.call(this, 'hy') this.age = 20; } var instance = new SubType() // 存在的问题:方法都在构造函数中定义,无法进行函数复用
组合继承(最常用)
将原型链和借用构造函数结合在一起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.constructor = SubType(); SubType.prototype.sayAge = function(){ alert(this.age) }
原型式继承
function object(o) { function F() {} F.prototypr = o; return new F(); } // ECMAScript5 通过Object.create()规范了原型式继承
寄生式继承
function craete(original){ var clone = object(original); // object()不是必须的,任何能够返回新对象的函数都可以 clone.sayHi = function() { alert('Hi') } return clone; } // 和构造函数类似
寄生组合式继承
function inherit(SubType, SuperType){ var prototype = object(SuperType.prototype); // 创建对象 prototype.constructor = SubType(); // 增强对象 subType.prototype = prototype; // 指定对象 }
- 原型链