特权方法、公有方法、静态方法
function Animal(name) { // Animal是一个基于函数的“类”
var _name = name; //私有属性
//特权方法
this.getName = function() {
return _name;
};
this.setName = function(name) {
_name = name;
};
}
Animal.type = function() {
console.log(this + 'is a private static function') // Animal的私有静态方法
}
Animal.prototype.speak = function() { // 定义一个原型方法 在js中也叫做公有方法 也是公有静态方法, 所有的Animal实例共享一份数据
console.log(this.name + ' makes a noise.');
}
var an1 = new Animal()
var an2 = new Animal()
console.log(an1.getName === an2.getName) // false
console.log(an1.speak === an2.speak) // true
console.log(an1.type) // undefined
console.log(an1.speak) // function() ……
如例子所示:Animal是一个基于函数的“类”,an1和an2都是一个Animal的对象实例
- 特权方法:每个对象独有的方法,每次创建一个实例对象,都会在内存中保存一份该特权方法,特权方法可以访问"类"的私有成员
- 公有方法:所有实例对象共享的一份属性和方法,只会在内存中保存一份
- 静态方法:可以定义在原型对象中,也可以定义在类上。定义在类上面的方法是类私有的,实例对象无法获取,定义在原型中的方法,实例对象可以获取
- 定义在函数内部的方法和属性是私有的,在函数外部获取不到,定义在函数上(如
Animal.type
)的方法和属性也是私有,不同之处是可以在外部通过Animal.type获取到
tips:将实例对象的属性存储在实例对象中,公有的属性存储在原型中,可以避免创建许多分公有的数据和方法,浪费存储空间(大部分情况下,方法都是公有的,存放在prototype中)
prototype和__proto__
如下所示,定义一个Animal类,Dog和Cat继承自Animal
function _inherits(subClass, superClass) {
subClass.prototype = Object.create(
superClass && superClass.prototype,
{
constructor: { value: subClass, enumerable: false, writable: true, configurable: true
}
});
subClass.__proto__ = superClass
}
function Animal(name) { // Animal是一个基于函数的“类”
}
var an1 = new Animal()
var Dog = function (_superClass) {
_inherits(Dog, _superClass)
function Dog() {
return _superClass.apply(this, arguments)
}
return Dog
}(Animal)
var Cat = function (_superClass) {
_inherits(Cat, _superClass)
function Cat() {
return _superClass.apply(this, arguments)
}
return Cat
}(Animal)
var dog1 = new Dog()
var cat1 = new Cat()
console.log(an1.__proto__ === Animal.prototype)
console.log(Dog.__proto__ === Animal)
console.log(Cat.__proto__ === Animal)
console.log(dog1.__proto__ === Dog.prototype)
console.log(cat1.__proto__ === Cat.prototype)
console.log(Dog.prototype.__proto__ === Animal.prototype)
console.log(Cat.prototype.__proto__ === Animal.prototype)
console.log(an1.constructor === Animal)
console.log(cat1.constructor === Cat)
console.log(dog1.constructor === Dog)
根据上述的输出,Animal、Dog、Cat之间的关系,以及他们的原
型关系如图所示:
总结出:
- 每个函数都有一个属性prototype,指向实例对象的原型
- 每个原型对象都有一个constructor,指向构造方法
- 每个对象都有一个__proto__,这项该对象的原型对象
a. 对象可以继承其原型对象中的方法和属性
b. __proto__有些浏览器不支持(开发中最好不要使用 __proto__),Object.getPrototypeOf(obj)是es5检索对象的原型(proto)的标准方法
c. 每个对象都有一个 __proto__指向它的原型,这就是原型链(prototype chain)
d.instanceof, object.instanceof(constructor),判断Object的原型链中是否存在constructor.prototype,表示一直继承关系
继续
console.log('\n', Animal.__proto__ === Function.prototype)
console.log(Animal.prototype.__proto__ === Object.prototype)
console.log(Function.__proto__ === Function.prototype)
console.log(Function.prototype.__proto__ === Object.prototype)
console.log(Object.__proto__ === Function.prototype)
console.log(Object.prototype.__proto__ === null)
继承
如上栗所示,Dog类继承Animal类,根据图中继承的关系,需要执行以下步骤
- Dog继承Animal:
Animal.apply(this, arguments)
,this
指向Dog对象 - 设置Dog.prototype继承Animal.prototype,并且设置Dog.prototype的constructor指向Dog函数:
Dog.prototype = Object.create(
Animal && Animal.prototype,
{
constructor: {
value: subClass,
enumerable: false,
writable: true,
configurable: true
}
});
- 设置
Dog.__proto__ = Animal
上栗babel的extends源码 -
\_classCallCheck
函数,判断生成的对象原型链上是否存在Constructor.prototype,避免把构造函数当做一般函数使用,比如把直接调用Animal()
就会报错'Cannot call a class as a function' -
\_inherits
函数,设置subClass. prototype.__proto__ = superClass以及subClass. prototype. constructor = constructor,设置subClass.__proto__ = superClass,.__proto__ 是非标准方法,浏览器不支持的时候使用Object.setPrototypeOf(subClass, superClass)
'use strict';
function _possibleConstructorReturn(self, call) {
if (!self) {
throw new ReferenceError('this hasn\'t been initialised - super() hasn\'t been called');
}
return call && (typeof call === 'object' || typeof call === 'function') ? call : self;
}
function _inherits(subClass, superClass) {
if (typeof superClass !== 'function' && superClass !== null) {
throw new TypeError('Super expression must either be null or a function, not ' + typeof superClass);
}
subClass.prototype = Object.create(superClass && superClass.prototype,
{ constructor: { value: subClass, enumerable: false, writable: true, configurable: true }
});
if (superClass) {
Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass;
}
}
function _classCallCheck(instance, Constructor) {
if (!(instance instanceof Constructor)) {
throw new TypeError('Cannot call a class as a function');
}
}
var Animal = function Animal(name) {
_classCallCheck(this, Animal);
this.name = name;
};
var Dog = function (_Animal) {
_inherits(Dog, _Animal);
function Dog() {
_classCallCheck(this, Dog);
return _possibleConstructorReturn(this, (Dog.__proto__ || Object.getPrototypeOf(Dog)).apply(this, arguments));
}
return Dog;
}(Animal);