JS的面向对象

  最近刚开始看《JavaScrit设计模式与开发实践一书》,感觉着实不错,让我对JS的面向对象有了更多的认识,下面就来总结一下:

  首先要了解的一点是:JavaScript是动态类型语言,与Java,C++这些静态类型语言不同,它无需类型检测。

多态

  多态就是将“做什么”和“谁去做以及怎样去做”分离开,也就是将“不变的事物”与“可能改变的事物”分离开来,然后把不变的部分隔离出来,把可变的部分封装起来,这样即可实现:同一操作作用于不同的对象,可以产生不同的解释和不同的执行结果。

  多态最根本的作用就是通过把过程化的条件分支语句转化为对象的多态性,从而消除这些条件分支语句。

封装

  因为JavaScript没有一些语言中的public,private,protected等关键字,所以只能依赖变量的作用域来实现封装特性,而且只能模拟出public和private这两种封装性。

  一般通过函数来创建作用域(如闭包就是实现了公开方法访问私有变量),但最新的ES6提供了let关键字,和Symbol创建私有属性,现在我还没看,等着一定会去了解。

继承

  关于原型链继承请参考博客“JS中new运算符详解”和高程6.3节(一定要先搞懂跟new有关的构造器模式,再去看原型链)

  JavaScript是一门基于原型的面向对象语言,它的对象系统就是使用原型模式来搭建的,所以它绝大部分的遵守原型编程范型的基本规则,即:

  1. 绝大部分的数据都是对象
  2. 要得到一个对象,不是通过实例化类,而是找到一个对象作为原型并克隆
  3. 对象会记住它的原型
  4. 如果对象无法响应某个请求,它会把这个请求委托给它的构造器的原型

1. 绝大部分的数据都是对象

  JavaScript中的根对象是Object.prototype对象,它是一个空对象,所有的对象都是从Object.prototype对象克隆而来的,Object.prototype对象就是它们的原型。

2. 要得到一个对象,不是通过实例化类,而是找到一个对象作为原型并克隆它

  JavaScript的函数既可以作为普通函数被调用,也可以被new运算符作为构造器被调用,其中的new运算符实际上就是克隆了一个原型的过程。

  关于new运算符的内部具体操作请见我博客中的“JS中new运算符详解”!

3. 对象会记住它的原型

  JavaScript给对象提供了一个名为proto的隐藏属性(这是浏览器的实现,规范里应该是不可访问的[[prototype]]),该属性默认指向它的构造器的原型对象。

  所以proto就是对象跟“对象构造器的原型”联系起来的纽带。

4. 如果对象无法响应某个请求,它会把这个请求委托给它的构造器的原型

  JavaScript的对象最初都是由Object.prototype对象克隆而来的,乍一看我们只能得到单一的继承自Object.prototype的对象,但对象构造器的原型并不仅限于Object.prototype上,而是可以动态指向其他对象。

  所以我们一般通过设置构造器的prototype来实现原型继承:

1
2
3
4
5
6
var A=function(){};
A.prototype={name:"Daoma666"};
var B=function(){};
B.prototype=new A();
var b=new B();
console.log(b.name);//Daoma666

  在执行时,引擎做了什么事:

  1. 首先尝试遍历对象b中所有属性,但没有找到name这个属性
  2. 查找name属性的请求被委托给对象b的构造器原型,它被b.proto记录着并且指向B.prototype,即new A()创建出来的对象,即一个指向构造器A的prototype的对象。
  3. 然而在该对象中依然没有找到name属性,于是请求继续委托给这个对象构造器的原型A.prototype。
  4. 然后在这里找到了name属性,并返回它的值

  最后我们还要注意一点,如果在A.prototype也没找到name属性时,请求会被传递给A.prototype的构造器原型Object.prototype,因为根对象是个空对象,且原型是null,所以这次请求就到此为止,b.name返回undefined。


  现在我们就能理解ECMAScript5提供的克隆对象的Object.create方法了,它天然的实现了原型继承。

1
2
3
4
5
Object.create=function(obj){
var F=function(){};
F.prototype=obj;
return new F();
}

  即上面的代码可以这样实现:

1
2
3
4
var A=function(){};
A.prototype={name:"Daoma666"};
var b=Object.create(new A());
console.log(b.name);//Daoma666

  此外还有需要注意的几点:

  1. Object.create()来创建对象比通过构造器创建对象要慢
  2. 设置构造器的prototype实现原型继承时,除了根对象,任何对象都会有一个原型;但通过Object.create(null)可以创建出没有原型的对象。
文章目录
  1. 1. 多态
  2. 2. 封装
  3. 3. 继承
    1. 3.0.1. 1. 绝大部分的数据都是对象
    2. 3.0.2. 2. 要得到一个对象,不是通过实例化类,而是找到一个对象作为原型并克隆它
    3. 3.0.3. 3. 对象会记住它的原型
    4. 3.0.4. 4. 如果对象无法响应某个请求,它会把这个请求委托给它的构造器的原型
,