面向对象的基本概念:类和实例。JavaScript不区分类和实例,通过原型来实现面向对象。
1. 类--->原型对象
JavaScript是一种直译式脚本语言,是一种动态类型、弱类型、基于原型的语言,内置支持类型。这种动态语言,是指程序在运行时可以改变其结构:新的函数可以被引进,已有的函数可以被删除等在结构上的变化。
本身不提供class实现(在ES2015/ES6中引入class关键字,但只是语法糖,JavaScript仍然是基于原型的)。
首先,我知道:
人——小红
动物——狗
....
类就是对象的类型模板,它集合了所有共性,是抽象出来的。那么我们怎么来创建一个具体的对象呢?这有一个过程:
(1)基于{...}对象
var people= {
name: '小红',
height: 1.6,
run: function () {
console.log(this.name + ' is running...');
}
}
这里我定义了一个叫小红的人,她有名、身高和跑步的特性。这些特性是人都有吧。我们可以继承她的特性,前面讲过js是通过原型来实现面向对象。
所以,基于people,我们要创建一个叫小强的人:
var xiaoqiang= {
name: '小强'
}
xiaoqiang.__proto__=people;(实际上,不要用obj.__proto__改变一个对象的原型,低版本IE也无法使用__proto__ )
把xiaoqiang的原型指向了对象people,根据原型链的思路,xiaoqiang.height和xiaoqiang.run在xiaoqiang里找不到,找上层原型,所以实现了一种“继承”。
JavaScript没有class的概念。所有对象都是实例。继承关系即一个对象的原型指向另一个对象。
(2)Object.create()
这个方法可以传入一个原型对象,并创建一个基于该原型的新对象。以上可封装函数为:
function createPeople(name){
//基于people 创建一个具体的人对象
var p = Object.create(people);
//并通过传入的值更新为这个人的具体特性
p.name = name;
return p;
}
var xiaoqiang=createPeople('小强')
xiaoqiang.__proto__===people //同理
(3)构造函数
JavaScript对每个创建的对象都会设置一个原型,指向它的原型对象。
这里,先好好说说原型链。
当我们用obj.xxx访问一个对象的属性时,JavaScript引擎先在当前对象上查找该属性,如果没有找到,就到其原型对象上找,如果还没有找到,就一直上溯到Object.prototype对象,最后,如果还没有找到,就只能返回undefined。
如:
arr——>Array.prototype——>Object.prototype——>null
或是自定义函数func,Function.prototype定义了apply()方法,所以所有函数都可以调用apply()方法
func——>Function.prototype——>Object.prototype——>null
前面两种方法创建对象,这里是用一种构造函数的方法创建对象,先定义构造函数:
function People(name){
this.name = name;
this.run =function () {
console.log(this.name + ' is running...');
}
}
可以看出这就是一个普通函数,我其实就是将第一个字母大写而已。
new People(),它就是构造函数,所绑定的this指向新创建的对象,默认返回this;不写new,是普通函数,返回undefined。
var xiaoqiang = new People('小强');
好了,把小强创造出来了!(o゚▽゚)o
同样,我们看看它的原型链:
xiaoqiang——>People.prototype——>Object.prototype——>null
constructor
原型链上的都有一个constructor属性,指向构造函数。
xiaoqiang.constructor === People.prototype.constructor; // true
People.prototype.constructor === People; // true
Object.getPrototypeOf(xiaoqiang) === People.prototype; // true
xiaoqiang instanceof People; // true
People有属性prototype指向xiaoqiang的原型对象。xiaoqiang这个对象是没有prototype属性的。
前面讲到继承关系即一个对象的原型指向另一个对象。所以如图:
改进方法:
前面run这个方法,写在构造函数里面的。new创建多个对象,多个对象都有自己的一个run函数,这里要做到的是共享同一个函数,即把方法都放到原型上去。
People.prototype.run=function(){
console.log(this.name + ' is running...');
}
最后,封装了一个实例化对象的函数:
function createPeople(props) {
return new People(props || {})
}
2.继承
传统的class继承应该是扩展一个已有的class,生成新的class。可是JavaScript不存在class类型,是采用原型继承的( ̄. ̄) ,怎么办呢?
办法是,基于People扩展出PrimaryPeople,那么定义这个构造函数:
function PrimaryPeople(props){
People.call(this, props);
this.age=props.age || 1;
}
这时候原型链是:
new PrimaryPeople——>PrimaryPeople.prototype——>Object.prototype——>null
这原型链就不对啊,应该继承People,用个空函数F作桥接,封装为:
function inherits(Child, Parent) {
var F = function () {};
F.prototype = Parent.prototype;
Child.prototype = new F();
Child.prototype.constructor = Child;
}
3. class
以上的继承蛮费解,在ES6开始引入了关键字class到JavaScript中!
编写类这样写:
class People{
constructor(name){
this.name = name;
}
run(){
console.log(this.name + ' is running...');
}
}
创建是同样的
var xiaoqiang=new People('小强');
而继承直接通过extends实现:
class PrimaryPeople{
constructor(name, age){
//用super调用父类的构造方法
super(name);
this.age = age;
}
newfunc(){
}
}
若是不支持ES6的class,使用工具Babel将class代码转换为传统的prototype代码