面向对象程序设计

昨天去知道创宇面试的时候面试官当时提了这样的问题,你对对象的属性了解有哪些,当场我说了知道 Enumerable Value Writable 然后说就记得这些了。但是面试官当时就又问那如果想知道我改变了对象中的值应该如何呢?当时我就想到了 MVVM 可当时却没有与 get set联系到一起,当时就只能尬聊了。

深入响应式原理

回去后把很久之前看过的《JavaScript高级程序设计》第 6 张重新看了遍。才发现原来自己把很多基础的东西都忘了。

理解对象

对象的属性类型

  • 数据属性
    [[Configurable]] [[Enumerable]] [[Writable]] [[Value]]
  • 访问器属性
    [[Get]] [[Set]] [[Configurable]] [[Enumerable]]
    访问器属性必须通过 Object.defineProperty() 来进行定义
    定义多个属性使用 Object.defineProperties()
    Vue 的响应式就离不开访问器属性,但是 Object.defineProperty() 这东西又支持的 IE9+,IE8部分支持这也就是为什么 Vue 不支持 IE8 以及更低版本浏览器的原因。
    如何监听 js 中变量的变化?
    读取属性的特性使用 Object.getOwnPropertyDescriptor()

创建对象

工厂模式

1
2
3
4
5
6
7
8
9
10
11
function creatPerson(name, age){
var a = new Object()
a.name = name
a.age = age
return a
a.say(){
alert(hi)
}
}
var p1 = creatPerson('aa', 10)
var p2 = creatPerson('bb', 20)
  • 解决了多个对象相似的问题
  • 未解决怎样知道对象的类型
  • say()这个方法没必要每个人都使用不一样的地址

构造函数模式

1
2
3
4
5
6
7
8
9
function Person(name, age){
this.name = name
this.age = age
this.sayName(){
alert(this.name)
}
}
var p1 = new Person('aa', 10)
var p2 = new Person('bb', 20)
  • 主要问题是每一方法都要每个实例上重新创建一边

原型模式

1
2
3
4
5
6
7
8
9
function Person(){}
Person.prototype.name = 'aa'
Person.prototype.age = 10
Person.prototype.sayName() = function(){
alert(this.name)
}
var p1 = new Person()
p1.sayName()
  • 这样就解决了之前的每一方法都要每个实例上重新创建一便但是若要改变 name 那所有实例的名字都将改变

理解原型对象

无论什么时候,只要创建一个新函数就会根据一组特定的规则为该函数创建一个 prototype 属性,这个属性指向函数的原型对象。在默认情况下所有原型对象都会获得一个 constructor 属性,这个属性是一个指向 prototype 属性所在函数的指针。

虽然所有实现中都无法访问[[Prototype]],可以用 isPrototypeOf() 以及 Object.getPrototypeOf()

hasOwnProperty()可以检测一个属性是否存在于实例中

Object.keys() 可以取得对象上可枚举的实例属性

Object.getOwnPropertyNames() 可以取得对象上所有实例属性包含不可枚举

更简单的原型语法

使用对象字面量来重写原型对象

1
2
3
4
5
6
7
8
9
function Person(){}
Person.prototype = {
//constructor: Person,
name: 'aa',
age:10
sayName: function(){
alert(this.name)
}
}
  • 确实是更方便,但是有一个问题,constructor 属性不再指向 Person 了,我们使用的语法本质上完全重置了 prototype 对象,所以 constructor 属性也就变成了新对象的 constructor 属性(指向 Object)当然我们可以特意设回适当的值(但这又会改变[[Enumerable]], 使用 Object.defineProperty()设置)

组合使用构造函数模式和原型模式

1
2
3
4
5
6
7
8
9
10
11
12
13
function Person(name, age){
this.name = name
this.age = age
}
Person.prototype = {
constructor: Person
sayName: function(){
alert(this.name)
}
}
var p1 = new Person('aa', 10)
var p2 = new Person('bb', 20)
  • 使用最广泛,认同度最高

动态原型模式

1
2
3
4
5
6
7
8
9
function Person(name, age){
this.name = name
this.age = age
if(typeof this.sayName != 'function'){
Person.prototype.sayName = function(){
alert(this.name)
}
}
}

寄生构造函数模式

很像工厂模式与构造函数模式

1
2
3
4
5
6
7
8
9
10
function Person(name, age){
var a = new Object()
a.name = name
a.age = age
a.sayName = function(){
alert(his.name)
}
return a
}
var p1 = new Person('aa', 10)
  • 返回的对象与构造函数或则与构造函数的原型属性没有联系,也就是说,构造函数返回的对象与在构造函数外部创建的对象没有什么不同。

稳妥构造函数模式

指没有公共属性,其方法也不引用 this

1
2
3
4
5
6
7
8
9
10
11
12
function Person(name, age){
var a = new Object()
this.name = name
this.age = age
a.sayName = function(){
alert(name)
}
return a
}
var p1 = Person('aa', 10)
p1.sayName()
  • 这种模式除了 p1.sayName() 可以访问到 name 其他方式都不行
棒棒糖