创建对象几种方法
原型链类(原型、构造函数、实例、原型链)
构造函数
- 定义把函数放在new操作符后面的函数叫构造函数。
任何函数,只要通过new操作符调用,就是构造函数,如果不是new调用的,则和普通函数不会有什么两样。
- 一般写构造函数的名字时,首字母是大写
- 所有的引用类型都是有构造函数的
var a = {} // 其实是var a =new Object()的语法糖
var a = [] // 其实是var a =new Array()的语法糖
function Foo(){...} // 其实是var Foo = new Function(){....}
原型、实例
首先看下面代码:
function Person(name) {
this.name = name
}
var m = new Person('我爱罗')
- new Person创建了一个实例m【就是一个具体化的产品】。
- 在声明函数Person的时候,JS引擎会给函数自动增加一个属性prototype,初始化为空对象,也就是原型对象即原型。
问:那我怎么知道这个原型对象是被哪个构造函数引用?
答:实际上原型对象上有个构造器(constructor),构造器会默认为你声明的函数。
原型链
从实例对象往上找构造这个实例的原型对象,然后再往上找原型对象之上的原型对象,以此类推一直找到Object.prototype(原型链顶端)
Person.prototype.say = function () {
console.log('hi')
}
var m1 = new Person('李洛克')
如果实例中没有这个方法则会通过__proto__ 到原型对象上找,如果还是没有再往上,以此类推。如果到Object.prototype还是没有找到,则报错没定义这个方法。
这个里面也可以看到构造函数中会增加了很多方法和属性,当我们创建多个实例的时候,可以避免每个都copy一份,而是存在他们共同的地方-原型对象。
温馨提示: 创建对象使用Object.create方法为什么返回时空?
因为Object.create是把参数作为一个新对象的原型对象赋给实例,实例本身不具备属性,只能通过原型链查找。
原型链类总结:
原型规则:
- 所有引用类型都具备对象特性,即可自由扩展属性。
var obj = {};obj.a=100
var arr = []; arr.a =100
function fn() {}
fn.a=100
- 所有引用类型都有一个__proto__(隐式类型)属性,属性是一个普通对象。
- 所有函数都有一个prototype(显式类型)属性,属性值是一个普通对象。
- 所有引用类型的__proto__属性值指向(完全等于)它的构造函数prototype属性值
- 当试图得到一个对象的属性或方法时,如果本身没有,则会从它的__proto__寻找,一直找到原型链顶点。
instanceof原理
继续拿这个为例子
function Person(name) {
this.name = name
}
var m = new Person('我爱罗')
console.log(m instanceof Person) // true
m instanceof Person原理是,m的__proto__一层层往上,能否对应到Person.prototype,找到则返回true。
这种判断也不时很严谨因为,m instanceof Object也是true。最严谨的方法是用构造器(constructor)
new 运算符
- 一个新对象被创建,它继承这个函数的原型对象(foo.prototype)。
- 构造函数foo被执行。执行的时候,相应的传参会被传入,同时上下文(this)会被指定为这个新实例。new foo 等同与new foo(),只能用在不传递任何参数的情况。
- 如果构造函数返回了一个对象,那么这个对象会取代整个new出来的结果。如果构造函数没有返回对象,那么new出来的结果为步骤1创建的对象。
var new2 = function (func) {
var o = Object.create(func.prototype)
var k = func.call(o)
if (typeof k === 'object') {
return k
} else {
return o
}
}
var m2 = new2(Person)