对象的创建
在JavaScript中创建一个对象有三种方式。可以通过对象直接量、关键字new和Object.create()函数来创建对象。
1. 对象直接量
创建对象最直接的方式就是在JavaScript代码中使用对象直接量。在ES5中对象直接量是由若干 名/值
组成的映射表, 整个映射表由{}
包含起来。每个名/值中间使用:
进行分割,名/值之间使用,
进行分割。
var o1 = {};
var o2 = {name: 'javascript'}
var o3 = {title: 'object', o2: o2}
// 数组、日期、函数、正则等作为特殊的对象,这里暂不讨论
// ES6 也暂时不讨论
上面就是使用对象直接量创建对象,这种方式比较简单方便。
2. 通过new创建对象
通过关键字new + 函数调用,就可以创建一个新的对象。被调用的函数被称为构造函数。 根据高程
中描述,使用 new + 调用函数 创建一个对象,这种方式会经历以下 4 个步骤:
(1) 创建一个新对象;
(2) 将构造函数的作用域赋给新对象(因此 this 就指向了这个新对象);
(3) 执行构造函数中的代码(为这个新对象添加属性);
(4) 返回新对象。
举个例子:
var Foo = function(name) {
this.name = name
}
var foo = new Foo('我');
/*
var foo = new Foo('我')
创建对象大致的流程是
(1) var obj = {};
`高程`中步骤(2),(3)如果不清楚的小伙伴,可以参考下面的实现
(2)、(3) Foo.call(obj); call 方法能够改变函数Foo的执行上下,把this指向obj,然后执行Foo函数
(4) foo = obj;
*/
typeof foo; // "object"
foo.constructor === Foo // true
3. 通过Object.create() 创建对象
这里是ES5官方提供的一个创建对象的方法。
var obj = {name: 'javascript'};
var newObj = Object.create(obj);
newObj.name // => javascript
原型
JS中每个函数都可以看成一个对象,而原型(prototype)就是函数中的其中一个属性。这里要很清楚,原型是函数上面的一个属性,这个属性只有函数对象才能拥有,别的类型是没有prototype属性。而原型的作用就是它所引用的对象能够被拥有它的函数所构建的实例化对象所访问。
那么原型是怎么和对象建立联系的?
编写代码如下:
let obj = {name: 'javascript'};
console.log(obj.name) // => javascript
console.log(obj)
控制台输出如下:
我们在程序中定义一个JavaScript对象,然后打印这个对象,这里除了前面定义的name
属性外,还有另外一个__proto__
属性。前面说道 函数 上面的 prototype(原型)所指向的对象能够拥有它的函数所构建的实例化对象所访问。至于具体怎么访问的细节没有说明。其实就是通过__proto__
这个属性作为桥梁进行的联接。
let obj = {name: 'javascript'};
console.log(obj.__proto__ === Object.prototype) //true
对比发现__proto__
所指的对象和Object.prototype所指的是一样的。我们是可以认定__proto__就是这座桥梁,那么obj就能访问到Object.prototype所指的对象就是理所当然了。
于是我就在猜测在使用直接定义量去定义对象的时候,在底层的实现很有可能就是通过new Object()的这种方式实现的。
于是我编写了下面的测试代码:
let obj = {name: 'javascript'};
let obj1 = new Object({name: 'javascript'});
发现上面的obj与obj1两者数据结构基本一致。
原型对象
原型对象简单来说就是函数的原型所指向的对象。前面说原型的时候,说了Object.prototype所指对象就是Object(函数)的原型对象。 在每个函数的原型对象中,默认会有constructor属性,用于指向函数本身。
Object.prototype.constructor === Object // true
let Test = function() {console.log('test')};
Test.prototype.constructor === Test // true
在最开始的时候,原型对象的constructor设计主要是为了获取对象
的构造函数。后来发现constructor属性易变,不可信。推荐使用instanceof。
var Test = function() {console.log('test')};
var test = new Test();
console.log(test.constructor); // Test
test.constructor = Object;
console.log(test.constructor); // Object
/*这里想使用 test.constructor 来判断是否是Test的实例化对象就不可信。而应该使用 instanceof */
test instanceof Test // true
原型对象有什么作用,主要实现对象的继承。
例如我们常用的对象、数组、函数都是得益于原型。
当我们使用变量直接量定义一个对象的时候,其实我们是没有定义它上面的这些能够调用的方法
let obj = {};
这些方法怎么来的,就是通过调用Object上面的原型对象而来的。
console.log(Object.prototype)
同理数组(通过调用Array.prototype),函数(通过调用Function.prototype)
原型链
ECMAScript中描述了原型链的概念,并将原型链作为实现继承的主要方法。其基本思想是利用原 型让一个引用类型继承另一个引用类型的属性和方法。简单回顾一下构造函数、原型和实例的关系:每个构造函数都有一个原型对象,原型对象都包含一个指向构造函数的指针,而实例都包含一个指向原型对象的内部指针。那么,假如我们让原型对象等于另一个类型的实例,结果会怎么样呢?显然,此时的 原型对象将包含一个指向另一个原型的指针,相应地,另一个原型中也包含着一个指向另一个构造函数 的指针。假如另一个原型又是另一个类型的实例,那么上述关系依然成立,如此层层递进,就构成了实 例与原型的链条。这就是所谓原型链的基本概念。