都说JS是一种class free语言.但是目前对这个认知还不是很深刻.以下一种模拟classd的方法,平时也经常用:
var Car = function(name){
this.name = name;
}
Car.prototype.run = function() {
console.log('Woonnnnnnn');
}
myCar = new Car('BYD');
myCar.name // 'BYD'
myCar.run // 'Woonnnnnnn'
这里发生的事情是这样的:
(1)使用关键词 new来调用函数
这种调用方式称为Construct Invocation.
任何函数使用Construct Invocation调用方式都会新生成一个object. 这个新生成的对象就是指向被调用函数(即Car)的prototype.所以这里myCar才会有run这个方法可以调用.
被调用函数的函数体中的this指向这个新创建的对象.所以这里myCar会有name属性.
(tip: 所有的object都是由construcot function创建) 其实每个function创建时都和两个看不见的属性息息相关: 1)the function's context 2)the code that implements the function's behavior.
每个function创建时都会有一个prototype属性(这个很重要).你可以这样拿到它:
function greet(){
alert('Heloo, my friend.');
}
greet.prototype
// Object {}
greet.prototype.constructor
// function(){alert('Hello, my friend');}
它的值是一个object.这个object有一个名为constractor
的key,值是这个function本身.(注:这里的prototype和Function.prototype有所不同)
很久以前就听过这句话:
当你读取一个objectA的属性时,如果它本身没有定义这个属性,就会继续往上到objectA.prototype所对应的object的上去找,如果还没找到则继续直到Object.prototype所对指向的那个object.
现在我们来找看看objectA的prototype是怎么来的吧.
在JavaScript中所有的object都是由构造函数构造出来的(其实这也给js一直声称一个object可以从另一个object继承属性带来疑惑的地方).在构造的过程中JavaScript会读取这个构造函数的prototype
属性.然后这个prototype对应的object就会成为新的object.通过JavaScript中this
值在不同调用方法下对应不同对象的特性,这个新object可以通过这种方式来继承原来object.来模拟class.
面向对象的语言中,我们使用类来创建一个自定义对象 类定义了一类事物公共的行为和方法;而实例则是类的一个具体实现。面向对象编程有三个重要的概念 - 封装、继承和多态. 如何在class free的JavaScript中模拟类和继承.
// 构造函数
function Person(name, set){
this.name = name;
this.sex = sex;
}
// 定义Person的原型,原型中的属性可以被自定义对象引用
Person.prototype = {
getName: function(){
return this.name;
},
getSex: function(){
return this.sex;
}
}
我们把Person称为构造函数,也就是创建自定义对象的函数.可以看出,JavaScript通过构造函数和原型的方式模拟了类的功能.创建自定义对象:
var chen = new Person('ChenLiang', 'man');
var yu = new Person('')
下面的例子将创建一个雇员类Employee,它从Person继承了原型prototype中所有属性.
function Employee(name, sex, employeeID) {
this.name = name;
this.sex = sex;
this.employeeID = employeeID;
}
// 将Employee的原型指向Person的一个实例
// 因为Person的实例可以调用Person原型中的方法, 所以Employee的实例也可以调用Person原型中的所有属性。
Employee.prototype = new Person();
Employee.prototype.getEmployeeID = function() {
return this.employeeID;
};
var zhang = new Employee("ZhangSan", "man", "1234");
console.log(zhang.getName()); // "ZhangSan
上面的继承实现很粗糙,并存在问题:
- 在创建Employee构造函数和原型(以后简称类)时,就对Person进行了实例化,这是不合适的。
- Employee的构造函数没法调用父类Person的构造函数,导致在Employee构造函数中对name和sex属性的重复赋值。
- Employee中的函数会覆盖Person中的同名函数,没有重载的机制(和上一条是一个类型的问题)。
- 创建JavaScript类的语法过于零散,不如C#/Java中的语法优雅。
- 实现中有constructor属性的指向错误,这个会在第二篇文章中讨论。
防止构造函数被非new方式调用导致污染全局变量的问题:
if( !(this instanceof arguments.callee) )
throw new Error('Constructor called as a function!')