由于Javascript的各种设计模式在实现上和传统的面向对象语言的实现相差很大,初学者或者后端转前端的小伙伴刚开始接触js时会一头雾水,本文把js重要的几个点摘出来,更简单直接的帮助大家去理解js的面向对象和实现。
文中除了对基础知识的介绍外,最大的篇幅用来介绍高阶函数及其应用。因为在js开发中闭包和高阶函数应用很多,许多设计模式也是通过闭包和高阶函数实现的,理解好闭包和高阶函数,有助于继续学习js的各种设计模式。
一 如何理解js的面向对象
1 js是基于原型的面向对象语言。与基于类的面向对象语言不同,
js中不存在类的概念,对象也并非从类中创建,所有的js对象都是从某个对象上克隆而来的,
我们在js中遇到的每个对象,实际上都是从Object.prototype(原型)克隆而来的。
因此克隆是创建对象的手段,原型继承的本质是基于原型链的委托机制。
2 js通过对封装、继承、多态、组合等技术反复使用提炼出一些可重复使用的面向对象设计技巧,而多态是重中之重。
二 如何理解this、call、apply
1、this总是指向对象:
● 作为对象的方法调用
● 作为普通函数调用
● 构造器调用
● call和apply调用
2、call和apply作用一模一样,区别在于传入参数形式不同
● apply接收两个参数,第一个参数指定了函数体内this对象的指向,第二个参数为带下标的集合
● call第一个参数相同,之后多个参数传入
因此,apply比call使用率更高。
三 如何理解闭包
1 闭包形成原因
● 变量作用域
● 变量生命周期
2 变量是搜索是由内而外,而非由外而内
3 形成方式:将一个函数定义在另一个函数内部,并将它暴露出来,要暴露一个函数,可将他返回或传给另一个函数。
4 闭包的作用
● 封装变量
● 延续局部变量的寿命
四、如何理解高阶
高阶函数需要至少满足其一:
- 函数可作为参数被传递
- 函数可作为返回值输出
1 函数作为参数被传递
1.1 回调函数
var getUserInfo = function(userId,callback){
$.ajax('http://xxx.com/getUerInfo?'+userId,function(data){
if(typeOf callback == 'function'){
callback(data);
}
})
}
getUserInfo(13157,function(data){
alert(data.userName);
});
在ajax异步请求中,回调函数使用的很频繁,当我们 想在请求之后做一些事情,又不知道具体的返回时间时,最常见的方法是把callback函数作为参数传入到ajax请求中,待请求完成之后执行callback函数。
1.2 Array.prototype.sort
[1,4,3].sort(function(a,b){
return a-b;
});
Array.prototype.sort接受一个函数做参数,这个函数里封装了数组元素的排序规则
2 函数作为返回值输出
函数作为返回值输出可以提现函数式编程的巧妙,让函数继续返回一个可执行的函数,意味着运算过程是可延续的。
var isType = function(type){
return function(obj){
return Object.prototype.toString.call(obj) === '[Object'+type+']';
}
}
var isString = isType('String');
var isArray = isType('Array');
var isNumber = isType('Number');
console.log(isArray([1,2,3]));
五 常见的高阶函数有哪些应用
1 面向切片编程AOP
AOP主要是把一些与核心业务逻辑模块无关的功能抽离出来,通常包括日志统计,安全控制,异常处理等,把这些抽离出来后再通过‘动态织入’的方式掺入业务逻辑模块中。
Function.prototype.before = function(beforefn){
var _self = this;
return function(){
beforefn.apply(this,arguments);
return _self.apply(this,arguments);
}
};
Function.prototype.after = function(afterfn){
var _self = this;
return function(){
var ret = _self.apply(this,arguments);
afterfn.apply(this,arguments);
return ret;
}
}
var func = function(){
console.log(2);
}
func = func.before(function(){
console.log(1);
}).after(function(){
console.log(3);
});
func();
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
2 currying 柯里化
currying又称部分求值,一个currying函数接受一些值,但不基于求值,而是继续返回另一个函数,刚才传入的参数在闭包中被保存起来,待到函数真正需要求值时将之前传入的参数一股脑的求值。
var currying = function (fn) {
var args = [];
return function () {
if(arguments.length === 0){
return fn.apply(this , args);
}else{
[].push.apply(args,arguments);
return arguments.callee;
}
}
};
var cost = (function () {
var money = 0;
return function () {
for(var i=0;i<arguments.length;i++){
money += arguments[i];
}
return money;
}
})();
var cost = currying(cost);
cost(100);
cost(200);
cost(300);
cost();
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
3 函数节流
少数情况下,函数的触发不是由用户主动触发,有可能被频繁调用,而造成很大的性能问题,如:
- window.onresize
- mousemove
- 上传进度
var throttle = function (fn,interval) {
var _self = fn,
timer,
firstTime = true;
return function () {
var args = arguments,
_me = this;
if(firstTime){
_self.apply(_me,args);
return firstTime = false;
}
if(timer){
return false;
}
timer = setTimeout(function () {
clearTimeout(timer);
timer = null;
_self.apply(_me,args);
},interval||500);
};
};
window.onresize = throttle(function () {
console.log(1);
},500);
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
将被即将执行的函数setTimeout延迟一段时间执行,如果这次延迟还没有完成,则忽视接下来的请求。
4 分时函数
假如我们要传建一个几千条的列表,每条信息占用一个节点,当在页面渲染这个列表时,有可能会让浏览器吃不消,往往出现浏览器卡死的现象,这时我们需要使用分时函数。
var timeChunk = function (ary,fn,count) {
var obj,
t;
var len = ary.length;
var start = function () {
for(var i=0;i<Math.min(count||1,ary.length);i++){
var obj = ary.shift();
fn(obj);
}
};
return function () {
t = setInterval(function () {
if(ary.length === 0){
return clearInterval(t);
}
start();
},500);
}
};
var ary = [];
for(var i=0;i<=1000;i++){
ary.push(i);
};
var renderList = timeChunk(ary,function (n) {
var div = document.createElement('div');
div.innerHTML = n;
document.body.appendChild(div);
},8);
renderList();