上帝说要有光! 于是有了光.
JavaScript 在 ES6 之前语法上还没有 "类",JavaScript 的开发者们在黑暗中苦苦摸索, 最终有了属于 JS 风格的面向对象编程风格.
类与对象的关系
类是对事物的抽象, 对象是类所描述的具体事物. 类与对象的关系就像汽车设计图与汽车实车的关系:
面向对象 (OOP) 的编程思维便是基于类与对象的编程."面向对象" 在软件工程的概念中有三个特征: 封装, 继承, 多态.
封装: 即是对所描述事物的抽象过程, 将其行为和属性存放于 "类";
继承: 方便被传宗接代, 父类可以方便的被子类 "占有" 它的属性和方法, 子类还可以拥有一些父类的没有的新技能;
多态: 同一操作作用于不同的对象, 可以有不同的解释, 产生不同的执行结果. JavaScript 由于自身语言的特征, 没有这一特征.
JavaScript 里的 "类"
JS 是基于对象的语言, 里面的任何东西几乎都是对象. 例如:
- var arr = [1,2]; // 字面量写法, 实际上等同于: var arr = new Array(1, 2);
- console.log(arr);
数组类型的变量实际上是一个 Array 类的实例. 我们所熟知的 Object 类型数据, Windows,document,DOM 节点都是对象.
类
JS 在实现 "类" 时是用的构造函数或者使用关键字 class(ES6 新增), 构造函数的编写就是上述特征之一的封装.
原型与原型链
JavaScript 的 "类" 有一个对象叫 "原型", 在类的属性 prototype 可访问到, 原型可定义该类所有实例 (对象) 所拥有的方法和属性, 所以我经常们可以把实例所共有的属性或方法存在 prototype 属性(下面会谈到).
我们还是以 "汽车" 为例来解释, 那么在代码中如何描述 "汽车"? 从国产某车企李总的话中有感而发:
汽车就是四个轮子加两个沙发
另外, 汽车肯定是会跑的. 所以 JS 的简单实现 "汽车" 的类如下:
- // ES5 写法(Es5 和 ES6 选其一写法)
- function Car(){
- this.wheel = 4;
- this.safa = 2;
- }
- Car.prototype.drive = function(){
- console.log('Wow~')
- }
- //ES6 写法 (前端们欣慰地发现, JS 终于有真正的 "类" 了)
- class Car {
- constructor() {
- this.wheel = 4;
- this.safa = 2;
- }
- drive(){
- console.log('Wow~')
- }
- }
现在 "造" 一辆车并让它 "跑" 起来:
- var car = new Car();
- console.log(car); // => Car {
- wheel: 4, safa: 2
- }
- car.drive(); // =>'Wow~'
再来看一下打印出来的 "car" 对象可以发现他还有个属性__proto__:
在绝大多数浏览器里对象的__proto__属性所指向的对象便是 Car 类的原型, 也就是:
car.__proto__ === Car.prototype; // => true
Car 的原型里有我们上面定义的 "drive" 方法, 以及有 constructor 属性用来表明类的构造函数.
需要注意的它还有个__proto__, 这个如何解释?
上面说到原型本身也是对象, 是对象就有原型, 然后就形成了原型链, 原型链的末端是 null:
- //Car 类的原型指向是 Object 类的原型,
- // 也就是说 car 的原型对象是 Object 的实例, 说起来有点绕...
- car.__proto__.__proto__ === Object.prototype; // => true
- // 原型链的结尾
- car.__proto__.__proto__.__proto__ // => null
他们的关系如下图:
来源: http://www.jianshu.com/p/b5548505f77d