Javascript 面向对象编程
概念¶
面向对象与面向过程¶
- 面向对象注重于抽象事物,面向过程更注重于叙述事物;
- 面向对象逻辑更加清晰有条理,面向过程对于简单逻辑更方便;
- JS 通过函数和原型,模拟类,而实现面向对象编程;
- 面向对象的编程思想,主要为实现三件事:封装,继承和多态。
基础知识¶
this¶
- 在函数执行时,会在函数内部创建两个变量: arguments, this
- arguments 是存储实参的一个类数组对象
- this 指向函数的执行上下文(谁调用,this指向谁)
function fn() {
console.log(this);
}
var a = {
b: fn,
c: {
d: fn
}
};
fn(); // window
a.b(); // a
a.c.d(); // a.c
箭头函数: this 指向的固定化,箭头函数表达式并不绑定自己的 this,arguments,super或 new.target。 箭头函数会捕获其所在上下文的 this 值,作为自己的 this 值;箭头函数会捕获其所在上下文的 this 值,作为自己的 this 值。 正是因为它没有this,所以也就不能用作构造函数。
call/apply¶
- 动态改变 this 指向
function fn() {
console.log(this);
}
var a = {
b: fn,
c: {
d: fn
}
};
fn.call(a.c); // a.c
Polyfill bind¶
var slice = Array.prototype.slice;
Function.prototype.bind = function() {
var thatFunc = this;
var thatArg = arguments[0];
var args = slice.call(arguments, 1);
return function() {
var funcArgs = args.concat(slice.call(arguments));
thatFunc.apply(thatArg, funcArgs);
}
};
new¶
function Cat(name) {
this.name = name;
}
var cat = new Cat('lanlan');
- new 做了哪些操作
创建一个空对象
将构造函数的
prototype
赋值给新对象的__proto__
属性将构造函数的
this
指向新对象执行构造函数
返回新对象
function Cat(name) {
this.name = name;
}
// var cat = new Cat('lanlan');
var cat =(function() {
var obj = {};
obj.__proto__ = Cat.prototype;
obj.constructor = Cat;
Cat.call(obj, 'lanlan');
return obj;
})();
编程思想¶
封装¶
- 使用工厂模式封装
function createCat(name) {
var obj = {};
obj.name = name;
return obj;
}
var cat = createCat('lanlan');
- 使用面向对象封装
function Cat(name) {
this.name = name;
}
var cat = new Cat('lanlan');
继承¶
// 父类
function Cat(name) {
this.name = name;
}
Cat.prototype.getName = function() {
return this.name;
};
组合式继承¶
function EnglishCat(name) {
Cat.call(this, name); // 父构造函数执行一次
this.type = 'English Cat';
}
EnglishCat.prototype = new Cat(); // // 父构造函数执行两次
var cat = new EnglishCat('lanlan');
console.log(cat);
组合式继承问题:
__proto__
里的属性多余,没有用
Cat
的构造函数执行了两次
寄生组合式继承¶
function EnglishCat(name) {
Cat.call(this, name);
this.type = 'English Cat';
}
// EnglishCat.prototype = Object.create(Cat.prototype);
// EnglishCat.prototype.constructor = EnglishCat;
function inheritPrototype(subClass, superClass) {
function F() {}
F.prototype = superClass.prototype;
subClass.prototype = new F();
subClass.prototype.constructor = subClass;
}
inheritPrototype(EnglishCat, Cat);
// 对比一下
// var wrapConstructor = function (NativeConstructor) {
// var Wrapper = function (a, b, c) {
// if (this instanceof NativeConstructor) {
// switch (arguments.length) {
// case 0: return new NativeConstructor();
// case 1: return new NativeConstructor(a);
// case 2: return new NativeConstructor(a, b);
// }
// return new NativeConstructor(a, b, c);
// }
// return NativeConstructor.apply(this, arguments);
// };
// Wrapper.prototype = NativeConstructor.prototype;
// return Wrapper;
// };
var cat = new EnglishCat('lanlan');
console.log(cat);
多态¶
相同方法,被不同对象调用,结果不同。
function Base() {}
Base.prototype.fn = function() { console.log('Base fn'); }
function SubA() {
this.fn = function() { console.log('SubA fn'); }
}
function SubB() {
this.fn = function() { console.log('SubB fn'); }
}
var subA = new SubA();
var subB = new SubB();
subA.fn();
subB.fn();