全面理解Javascript的面向对象(一)

由于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;
});
//输出[1,3,4]

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]));   //输出true

五 常见的高阶函数有哪些应用

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();

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();   //输出600 

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);

将被即将执行的函数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();

猜你喜欢

转载自blog.csdn.net/franktaoge/article/details/68922079