令人头疼的JavaScript对象

对象创建

创建对象的方式有两种

第一种是使用对象字面量的方式,也就是声明形式

let obj = {
    key: value
    //...
}

第二种是使用构造方式

let obj = new Object()
obj.key = value

这两种方式虽然形式不同,但产生的是同一种对象

在实际应用中我们也会使用构造函数来生成实例对象

function Person(name) {
  this.name = name
}

Person.prototype.say = function () {
  return 'I am ' + this.name
}

let p = new Person('feng')

在ES6中,我们还可以利用class关键字来创建对象

class Person {
  constructor(name) {
    this.name = name
  }

  say() {
    return 'I am ' + this.name
  }
}
typeof Person // "function"
Person === Person.prototype.constructor // true
let p = new Person('feng')
p.say === Person.prototype.say // true

我们可以把ES6的class看做是一种语法糖,ES6的类作为构造函数的另一种写法。ES6的类与构造函数的主要区别在于:ES6类必须使用new调用,而构造函数可以当作普通函数执行

属性

对象包含了存储在特定命名的位置上的任意类型的值,我们称这些值为属性。在对象中,属性名是字符串

let obj = {
    a: 1 // 属性
}

访问在obj对象中的a,有两种方法

第一种是使用.操作符,通常称为属性访问,只能访问与标识符相同命名规范的属性

obj.a // 1

第二种是使用[]操作符,通常称为键访问,可以访问任何兼容 UTF-8/unicode 的字符串类型的属性名

let a = 'a'
obj['a'] // 1
obj[a] // 1

属性描述符

在JavaScript中,对象的属性用属性描述符来表示。属性有四种性质:值、可写性、可配置性、可枚举性

let obj = {
	a: 1
}
Object.getOwnPropertyDescriptor( obj, "a" )
// { value: 1, writable: true, enumerable: true, configurable: true }
可写性

writablefalse时,属性变为不可写,也就意味着值不可改变

let obj = {}
Object.defineProperty( obj, "a", {
	value: 1,
	writable: false, // 不可写
	configurable: true,
	enumerable: true
} )
obj.a = 2
obj.a // 1
可配置性

configurablefalse时,就意味着我们不可以使用Object.defineProperty来修改该属性的描述符定义,同时也无法使用delete操作符对它进行删除

唯一例外的是,当configurablefalse时,writeable也可以从true变为false

let obj = {
	a: 1
}

Object.defineProperty( obj, "a", {
	value: 2,
	writable: true,
	configurable: false,
	enumerable: true
} )

Object.defineProperty( obj, "a", {
	value: 3,
	writable: true,
	configurable: true,
	enumerable: true
} ) // TypeError

Object.defineProperty( obj, "a", {
	value: 3,
	writable: false,
	configurable: false,
	enumerable: true
} )

obj.a // 3
delete obj.a
obj.a // 3
可枚举性

enumerablefalse时,就意味着这个属性不会在特定的枚举操作中出现

let obj = {
    a: 1,
    b: 2
}
Object.defineProperty( obj, "a", { enumerable: false } )
obj.a // 1
for (let k in obj) {
	console.log( k, obj[k] )
} // b 2

setter和getter

利用取值函数和存值函数可以拦截指定属性的存取行为

let obj = {
    b: 1,
    set a(value) {
        this.b = value*10
    }
}

obj.a = 1
obj.b // 10

Object.defineProperty( obj, "c", {
    get: function(){ return this.b * 2 },
    enumerable: true
} )
obj.c // 20

原型

在JavaScript中只有对象,没有类

对象之间用原型联系起来

__proto__prototype

prototype 是构造函数的属性,而 __proto__ 是对象的属性

每个对象的__proto__属性指向自身构造函数的prototype

Object.prototype.__proto__ // null
Object.__proto__ === Function.prototype // true
Function.__proto__ === Function.prototype //true
let obj = {}
obj.__proto__=== Object.prototype // true
Function.prototype // [Function]
Function.prototype.__proto__ === Object.prototype // true
function Foo() {}
Foo.__proto__ === Function.prototype //true
let f1 = new Foo()
f1.__proto__ === Foo.prototype // ture

虽然有一点不可思议,但是结合上述文字以及代码,我们可以得到如下结论:

  • 原型链的最顶端是内建的Object.prototype,在向上思考就是哲学的范畴了
  • Object对象是由Funtion函数创建出来的
  • Function是一种对象,因为对象是由函数创建出来的,所以它是Function函数创建出来的
  • 使用字面量的方式创建的对象是由Object对象创建的
  • Function.prototype指向一个对象,并且这个对象是由Object对象创建的
  • 一个自定义的函数是由Function函数创建的
  • 构造函数可以创建对象

简单的说,对象以自己为模版可以创建更多的对象,而函数既能创建函数又能创建对象,同时函数也是一种对象

链接

原型链的机制是一种存在于一个对象上的内部链接,它指向一个其他对象

原型链使得代码的复用性增强,同时对象的的属性的访问变得复杂起来,

let obj = {
    a: 1
}
let obj1 = Object.create( obj )
obj1.__proto__ === obj //true
obj1.a // 1
obj.b = 2
obj1.b // 2
Object.defineProperty( obj, "b", { writable: false } )
obj1.b = 3
obj1.b // 2
Object.defineProperty( obj, "c", {
    get: function(){ return this.b * 2 },
    enumerable: true
} )
obj1.c // 4
for (let k in obj1) {
	console.log(k, obj1[k])
}
/*
a 1
b 2
c 4
*/

obj对象为模板,创建出了obj1对象,或者说obj1对象的原型是obj对象。通过观察,我们可以推论,在对对象的属性进行访问时,先查找对象内部是否存在名称匹配的属性,如果不存在,就沿着原型链向上查找,直到找到匹配的属性,或者已经到达原型链的最顶端

构造器

构造器是什么不重要,重要的是它做了什么

function Foo() {}
Foo.prototype.constructor === Foo //true
function Bar() {}
Foo.prototype = new Bar()
Foo.prototype.constructor = Foo
let obj = new Foo()
obj // Foo{}
obj.__proto__.__proto__ === Bar.prototype // true

挖一个坑

由于javascript特有的原型机制,在实际项目中可能会遇到两种截然不同的设计方式,一种是传统的面向对象的设计方式,另一种则是我在《你不知道的JS》一书中所了解的面向委托的设计,读者感兴趣可以去自己去了解一下

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章