对象
多态:
- 简介: 同一操作用于不同对象可以产生不同的结果;
- 背后的思想:将不变的事与可能变的事分开,封装;关键是消除类型之间的耦合;
- 实现:
- 一般语言:常用继承,向上转型来表现对象的多态特性;
- JS:由于本身的的特征,不存在类型耦合的问题;
- 作用:通过把过程化的条件分支语句转化为对象的多态性,从而消除这些分支语句;
封装
- 目的:将信息隐藏
- 封装数据:
- 一般语言:语法解析实现,private、public、protected
- JS: 作用域实现,闭包模拟pblic、private
- 封装实现:
- 对象之间只通过API接口通信;对象内部变化对其他对象而言是不可见的;
- 封装类型:
- 把对象的真正类型隐藏在抽象类或者借口之后;
- 通过抽象类和接口进行的;
- 静态语言中重要封装方式,目前JS还没有能力/必要;
- 封装变化:
- 封装的主要体现
- 把系统中稳定部分和易变部分隔离,以后只替换易变的部分;能保证程序稳定性和扩展性
继承
类式继承
SuClass.prototype = new SuperClass();
- 所有子类实例的父类属性方法都是共用的;
- 实例化父类时无法对父类构造函数进行初始化;
构造函数继承
function SubClass (id) {
SuperClass.call(this. id);
}
组合继承
function SubClass (id) {
SuperClass.call(this. id);
}
SuClass.prototype = new SuperClass();
原型式继承
// Object.create
function inheritObj (obj) {
var F = function(){};
F.prototype = obj;
return new F;
}
寄生式模式
function inheritPro(SubClass, SuperClass) {
var p = inheritObj(SuperClass.prototype);
p.constructor = SubClass;
SubClass.prototype = p;
}
多继承/拷贝
var extend = function (target, source) {
for(var pro in source) {
target[pro] = source[pro];
}
return target;
}
var mix = function () {
var i = 1, len = arguments.length,
target = arguments[0], arg;
for(; i < len; i++) {
arg = arguments[i];
for(var pro in arg) {
target[pro] = arg[pro]
}
}
return target;
}
原型编程范型
- 所有数据都是对象;
- JS中有两套类型机制:基本类型和对象类型;
number、string、boolean
这几种基本类型可以通过包装类处理,所以除了undefiend
,其他都是对象
- 所有对象的根对象:
Object.prototype
(空对象)
- 根对象的原型是
null
- 要得到一个对象,不是通过实例化类,而是找到一个对象作为原型克隆他;
- JS中克隆是引擎内部负责的,不需要操作;
new
运算符创建对象时,实际上也只是先克隆根对象,再进行一些额外操作(引擎从内存考虑的额外操作)
- 对象会记住他的原型;
- 对象的
_proto_
属性指向{Constructor}.prototype
;除非修改_proto_
,不然是不会切断和原构造器原型的联系;
- 如果对象无法响应某个请求,会委托给自己的原型;
this
- 总是指向一个对象;具体是运行时基于函数的执行环境动态绑定的,而非函数被声明时的环境;
指向
- 普通函数调用时:指向
window
;
- 对象方法调用时:指向对象
- 如果对象方法里有局部方法,里面this会指向
window
- 在ES5
strict
模式下,this
指向window
的都会变成undefined
apply、call
时动态改变 this
丢失
var getId = document.getElementById;
document.getElementById = function (func) {
return function() {
return func.apply(document, arguments)
}
})(document.getElementById);
var getId = document.getElementById;
call、apply
修正this
模拟bind
Function.prototype.bind = function() {
var self = this,
context = [].shift.call(arguments),
args = [].slice.call(arguments);
return function() {
return self.apply(context, [].concat.call(args, [].slice.call(arguments)));
}
}
闭包
形成:
- 执行函数返回对象或函数并保留与执行函数内部变量的联系;
作用
- 封装变量:将只在改作用域的变量和固定的部分闭包封装,将执行运算的部分返回;
- 延长变量寿命:如for循环中;
与内存管理
- 循环引用:如果闭包的作用域链中保存着一些DOM节点,就有可能造成内存泄漏;
- 过多的闭包可能会造成内存问题,需要手动将变量引用设置为
null
高阶函数
作为参数传递
- 可以在分离业务代码的时候抽离一部分容易变化的业务逻辑放到函数参数中;
- 回调函数:
- 异步请求
- 当一个参数不适合执行一些请求的时候,将这些请求封装成一个函数
- 数组的高阶函数
作为返回值输出
函数柯里化:
- 接受一些参数后不会立即求值,而是继续返回另一个函数;
- 刚才传入的参数在函数形成的闭包中保存起来;待真正要求值时,之前的参数会一次性用于求值
- 因此函数内应有判断执行的条件;
函数节流
- 原因:被调用过频繁
- 解决:将被执行的函数用
setTimeout
延迟一段时间执行,如果改次延迟执行还没有完成,则忽略接下来调用改函数的请求
var throttle = function(fn, interval){
var func = fn, timer, firstTime = true;
return function() {
var args = arguments,
self = this;
if(firstTime) {
func.apply(self, args);
return firstTime = false;
}
if(timer)
return false;
timer = setTimeout(function() {
clearTimeout(timer);
timer = null;
func.apply(self, args);
}, interval || 500);
}
}
分时函数
- 在短时间内进行多次操作时,可以将其分成更短的时间内少量的操作;
var timeChunk = function(ary, fn, count) {
var obj, t, count = count || 1;
var start = function () {
for(var i = 0; i++ < Math.min(count, ary.length);) {
var obj = ary.shift();
fn(obj);
}
}
return function () {
t = setInterval(function () {
if(ary.length === 0)
return clearInterval(t);
start();
}, 200);
}
};
惰性加载函数
- 由于浏览器的兼容性,一些方法需要借用嗅探来设定;
- 一般是在一个函数里利用判断分支来兼容,但这样每次调用都会判断一次
- 还有方法是仅加载的第一次就进行嗅探工作,但可能并会一定用到这个方法
- 惰性加载就是在第一次调用的时候分支判断,同时重写这个函数;下次调用的时候就直接执行重写的函数;