Javascript Web Application笔记之MVC和类 – 类的继承 函数调用 匿名函数 作用域 类库介绍
本文由发表于5年前 | Javascript | 评论数 2 |  被围观 4,775 views+
1、基于原型的类继承:2、给类库添加继承:3、函数调用:3.1、访问原始上下文的方法:3.2、对访问原始上下文的改进:3.3、使用apply和call的原因:4、控制类的作用域:5、添加私有函数:6、类库:
1、基于原型的类继承:

当你读取一个对象的属性时,JavaScript 首先会在本地对象中查找这个属性,如果没有找到,JavaScript 开始在对象的原型中查找,若仍未找到还会继续查找原型的原型,直到查找到Object.prototype。如果找到这个属性,则返回这个值,否则返回undefined。

使用原型实现继承,首先需要定义一个父类的构造函数,然后,将父类的新实例赋值给构造函数的原型。下面使用原型实现继承:

var Animal = function(){};
    Animal.prototype.breath = function(){
    console.log('breath');
};

var Dog = function(){};
// Dog 继承了Animal
Dog.prototype = new Animal;
Dog.prototype.wag = function(){
    console.log('wag tail');
};
var dog = new Dog;
dog.wag();  // 调用子类的方法
dog.breath();  //调用继承的属性
2、给类库添加继承:
var Class = function(parent){
    var klass = function(){
        this.init.apply(this, arguments);
    };
    // 改变klass 的原型,通过匿名函数的方式让父类的原型赋值给子类
    if (parent) {
        var subclass = function() { };
        subclass.prototype = parent.prototype;
        klass.prototype = new subclass;
    };
    klass.prototype.init = function(){};
    // 定义别名
    klass.fn = klass.prototype;
    klass.fn.parent = klass;
    klass._super = klass.__proto__;  // _super,__proto__
    /* include/extend 相关的代码…… */
    return klass;
};

如果将parent 传入Class 构造函数,那么所有的子类则必然共享同一个原型。这种创建临时匿名函数的小技巧避免了在继承类的时候创建实例,这里暗示了只有实例的属性才会被继承,而非类的属性。

下面是生成由Animal继承的类,并创建该继承类的实例,调用继承下来的方法:

var Animal = new Class;
Animal.include({
    breath: function(){
        console.log('breath');
    }
});
var Cat = new Class(Animal)
// 用法
var tommy = new Cat;
tommy.breath();
3、函数调用:

和其他对象不同的是,函数是可调用的。函数内上下文,如this 的取值,取决于调用它的位置和方法。

除了直接调用函数之外,还有其他两种方法可以调用函数:apply() 和call()。两者的区别在于传入函数的参数的形式。

关于apply()和call()的详细使用,可以参考这篇文章的1.4函数上下文:

http://www.itzhai.com/javascript-notes-javascript-function.html
3.1、访问原始上下文的方法:

为了访问原始上下文,可以将this 的值存入一个局部变量中,这是一种常见的模式,比如:

var clicky = {
    wasClicked: function(){
        /* ... */
    },
    addListeners: function(){
        var self = this;
        $('.clicky').click(function(){
            self.wasClicked()
        });
    }
};
clicky.addListeners();
3.2、对访问原始上下文的改进:

这里提供给一个proxy匿名函数进行调用apply方法:

var proxy = function(func, thisObject){
    return(function(){
        return func.apply(thisObject, arguments);
    });
};
var clicky = {
    wasClicked: function(){
        /* ... */
    },
    addListeners: function(){
        var self = this;
        $('.clicky').click(proxy(this.wasClicked, this));
    }
};

实际上,jQuery 也包含了实现这个功能的API,你或许已经猜到了,就是jQuery.proxy() :

$('.clicky').click($.proxy(function(){ /* ... */ }, this));
3.3、使用apply和call的原因:

使用apply() 和call() 还有其他很有用的原因,比如“委托”。我们可以将一个调用委托给另一个调用,甚至可以修改传入的参数:

var App {
    log: function(){
        if (typeof console == "undefined") return;
        // 将参数转换为合适的数组
        var args = jQuery.makeArray(arguments);
        // 插入一个新的参数
        args.unshift("(App)");
        // 委托给console
        console.log.apply(console, args);
    }
};
4、控制类的作用域:

上面提供的proxy匿名函数是一个非常有用的模式。我们应当将其添加至我们的“类”库中。我们在类和实例中都添加proxy 函数,这样就可以在事件处理程序之外处理函数的时候和下面这段代码所示的场景中保持类的作用域:

var Class = function(parent){
    var klass = function(){
        this.init.apply(this, arguments);
    };
    klass.prototype.init = function(){};
    klass.fn = klass.prototype;
    // 添加一个proxy 函数
    klass.proxy = function(func){
        var self = this;
        return(function(){
            return func.apply(self, arguments);
        });
    }
    // 在实例中也添加这个函数
    klass.fn.proxy = klass.proxy;
    return klass;
};

类库中的proxy函数的使用:

var Button = new Class;
Button.include({
    init: function(element){
        this.element = jQuery(element);
        // 代理了这个click 函数
        this.element.click(this.proxy(this.click));
    },
    click: function(){ /* ... */ }
});

如果我们没有使用proxy 将click() 的回调包装起来,它就会基于上下文this.element来调用,而不是Button,这会造成各种问题。在新版本的JavaScript——ECMAScript 5(ES5)中同样加入了bind() 函数用以控制调用的作用域。

bind函数实际上等同于上面的proxy函数,其使用如下:

Button.include({
    init: function(element){
        this.element = jQuery(element);
        // 绑定这个click 函数
        this.element.click(this.click.bind(this));
    },
    click: function(){ /* ... */ }
});

但是对于一些老版本的浏览器,不支持bind()方法,如果需要兼容,则需要手动实现bind()函数的兼容性,下面就是一段实现了bind()函数的代码:

if (!Function.prototype.bind) {
    Function.prototype.bind = function (obj) {
        var slice = [].slice,
        args = slice.call(arguments, 1),
        self = this,
        nop = function () {},
        bound = function () {
            return self.apply( this instanceof nop ? this : (obj || {}),
            args.concat(slice.call(arguments)));
        };
        nop.prototype = self.prototype;
        bound.prototype = new nop();
        return bound;
    };
}
5、添加私有函数:

我们可以利用JavaScript 匿名函数来创建私有作用域,这些私有作用域只能在内部访问:

var Person = function(){};
(function(){
    var findById = function(){ /* ... */ };
    Person.find = function(id){
    if (typeof id == "integer")
        return findById(id);
    };
})();

我们将类的属性都包装进一个匿名函数中,然后创建了局部变量(findById),这些局部变量只能在当前作用域中被访问到。Person 变量是在全局作用域中定义的,因此可以在任何地方都能访问到。

定义变量的时候不要丢掉var 运算符,因为如果丢掉var 就会创建全局变量。如果你需要定义全局变量,在全局作用域中定义它或者定义为window 的属性:

(function(exports){
    var foo = "bar";
    // 将变量暴露出去
    exports.foo = foo;
})(window);
alert(foo);
6、类库:

jQuery 本身并不支持类,但通过插件的方式可以轻易引入类的支持,比如HJS(http://plugins.jquery.com/project/HJS)。HJS 允许你通过给$.Class.create 传入一组属性来定义类:

var Person = $.Class.create({
    // 构造函数
    initialize: function(name) {
    this.name = name;
    }
});

可以在创建类的时候传入父类作为参数,这样就实现了类的继承:

var Student = $.Class.create(Person, {
    price: function() { /* ... */ }
});
var alex = new Student("Alex");
alex.pay();

HJS 并不是我们的唯一选择,Spine(http://maccman.github.com/spine)同样实现了类,通过直接在页面中引入spine.js(http://maccman.github.com/spine/spine.js)来使用它。

如果你不想把视野局限于jQuery 的话,那就多关注一下Prototype(http://prototypejs.org/),它包含很多不错的API(http://prototypejs.org/learn/class-inheritance),并且是其他很多类库的灵感来源。

除了文章中有特别说明,均为IT宅原创文章,转载请以链接形式注明出处。
本文链接:http://www.itzhai.com/javascript-the-web-application-notes-mvc-and-class-class-inheritance-function-calls-the-anonymous-function-scope-class-library-introduced.html
关键字:
arthinking Java技术交流群:280755654,入门群:428693174 more
分享到:
 
2012 6/23
文章评论
    2条评论
  1. Aric 2012年09月15日21:50:55  #-49楼 回复 回复

    楼主看着这段代码有点不懂
    var proxy = function(func, thisObject){
    return(function(){
    return func.apply(thisObject, arguments);
    });
    };
    能否写成这样?
    var proxy = function(func, thisObject){
    return func.apply(thisObject, arguments);
    };

    • arthinking 2012年09月16日00:59:25 回复 回复

      这句话是将回调包装在另外一个匿名函数中,来保持原始的上下文。这里返回的是一个函数,用于作为事件的处理函数:
      this.element.click(this.proxy(this.click));

给我留言

有人回复时邮件通知我
Javascript的相关文章
随机文章 本月热门 热评
1 谷歌浏览器Chrome控制台提示Uncaught ReferenceError xl_chrome_menu is not defined 2011/7/28
2 Java Web笔记 – Sessoin的使用 2011/11/12
3 Java Web笔记 – Servlet中的Listener监听器的介绍 常用监听器接口 实现监听器 2011/11/12
4 Javascript Web Application笔记之MVC和类 – 类的继承 函数调用 匿名函数 作用域 类库介绍 2012/6/23
5 【转】在危机的边缘上 马斯克如何看待失败? 2013/11/4
6 Java基础笔记 – 通过反射机制修改类中的私有属性的值 2011/10/8
友情推荐 更多
破博客 文官洗碗安天下,武将打怪定乾坤。多么美好的年代,思之令人泪落。
Mr.5's Life 白天是一名程序员,晚上就是个有抱负的探索者
行知-追寻技术之美 关注大数据,分布式系统
我爱编程 编程成长轨迹
Cynthia's Blog 学习笔记 知识总结 思考感悟
 
猜您喜欢
欢迎关注我的公众号 IT宅
关于IT宅 文章归档

IT宅中的文章除了标题注明转载或有特别说明的文章,均为IT宅的技术知识总结,学习笔记或随笔。如果喜欢,请使用文章下面提供的分享组件。转载请注明出处并加入文章的原链接。 感谢大家的支持。

联系我们:admin@itzhai.com

Theme by arthinking. Copyright © 2011-2015 IT宅.com 保留所有权利.