前  言

JRedu

在之前的两篇博客中,我们详细探讨了JavaScript OOP中的各种知识点(JS OOP基础与JS 中This指向详解 、 成员属性、静态属性、原型属性与JS原型链)。今天我们来继续探讨剩余的内容吧。

 我们都知道,面向对象的三大特征——封装、继承、多态。 封装无非就是属性和方法的私有化,所以我们JS中提供了私有属性和私有方法。 而JS中并没有多态,因此我们说JS是一门基于对象的语言,而非面向对象的语言。 那么,面向对象三大特征中,在JS中最重要的就是继承了。

继承的基本概念

使用一个子类继承另一个父类,子类可以自动拥有父类的属性和方法。

>>> 继承的两方,发生在两个类之间。

所以,所谓的继承,无非就是让子类,拥有父类的所有属性和方法。那么,在JS中,我们要模拟实现这一步,有三种常用的方法可以实现。

分别是:扩展Object的prototype实现继承、使用call和apply实现继承、使用原型实现继承。

扩展Object的prototype实现继承

扩展Object实现继承的本质,是我们自己写了一个方法,将父类的所有属性和方法通过遍历循环,逐个复制给子类。

详细步骤如下

1:定义父类

function Parent(){}

2:定义子类

funtion Son(){}

3:通过原型给Object对象添加一个扩展方法。

Object.prototype.customExtend = function(parObj){
            for(var i in parObj){
                // 通过for-in循环,把父类的所有属性方法,赋值给自己
                this[i] = parObj[i];
            }
        }

4:子类对象调用扩展方法

Son.customExtend(Parent);

完整代码示例

// 1.定义父类
        function Person(name,age){
            this.name = name;
            this.age = age;
            this.say = function(){
                alert(this.name+":"+this.age);
            }
        }
        // 2.定义子类
        function Student(no){
            this.no = no;
            this.add = function(a,b){
                alert(a+b);
            }
        }
        function Programmer(lang){
            this.lang = lang;
            this.codding = function(){
                alert("我爱敲代码!敲代码使我快乐!");
            }
        }
        // 3.通过原型给Object对象添加一个扩展方法。
        Object.prototype.customExtend = function(parObj){
            for(var i in parObj){
                // 通过for-in循环,把父类的所有属性方法,赋值给自己
                   this[i] = parObj[i];
            }
        }

        var p = new Person("小明","18");
        var s = new Student("0001");
        s.customExtend(p);//现在s继承了p的所有属性和方法。

完整代码示例

使用call和apply实现继承

首先,要使用这种方式显示继承,我们再来回顾一下call和apply两个函数的作用:

call和apply:通过函数名调用方法,强行将函数中的this指向某个对象;

call写法:  func.call(func的this指向的obj,参数1,参数2...);

apply写法:func.apply(func的this指向的obj,[参数1,参数2...]);

那么,我们使用这两个函数实现继承的思路就是:在子类中,使用父类函数调用call或apply,并将父类的this,强行绑定为子类的this。 那这样,父类绑定在this上的属性和方法,不就顺利成章的绑定到子类的this上了吗?

详细步骤如下

1:定义父类

funtion Parent(){}

2:定义子类

function Son(){}

3:在子类中通过call方法或者apply方法去调用父类。

function Son(){

Parent.call(this,....); // 将父类函数中的this,强行绑定为子类的this

}

完整代码如下

function Person(name,age){
            this.name = name;
            this.age = age;
            this.say = function(){
                alert("我叫:"+this.name+";今年:"+this.age+"岁");
            }
        }

        function Student(no,stuName,stuAge){
            this.no = no;
            Person.call(this,stuName,stuAge);
            // 执行上述代码,相当于把Person类所有this替换为Student类this
            // 换言之,也就是把Person类所有属性和方法,全给了Student类
        }

        var stu = new Student(12,"zhangsan",14);
        stu.say();// 子类继承了父类say方法

完整代码

使用原型实现继承

使用原型实现继承,是比较简单而且比较好理解的一种,就是将子类的prototype指向父类的对象就可以啦。

详细步骤如下

1:定义父类

function Parent(){}

2:定义子类

function Son(){}

3:把在子类对象的原型对象声明为父类的实例。

Son.prototype = new Parent();

完整代码如下:

function Person(name,age){

this.name = name;

this.age = age;

this.say = function(){

alert("我叫:"+this.name+";今年:"+this.age+"岁");

}

}

function Student(no){

this.no = no;

}

Student.prototype=new Person("jh",14); //子类prototype指向父类对象

var stu = new Student(12);

stu.say();//继承后,子类获得了父类的全部属性方法

完整代码

闭包

要理解闭包,首先,我们要了解一下JS中的作用域:

1、JS中的作用域

全局变量:函数外声明的变量

局部变量:函数内声明的变量

在JS中,函数为唯一的局部作用域,而if、for等其他{}没有自己的作用域

所以,函数外不能访问局部变量。其实,变量在函数执行完毕以后,占用的内存就会被释放。

2、闭包

在概述中,我刚刚提到,面向对象的三大特征中的“封装”,我们可以用函数的私有属性来实现。这个私有属性,其实也就是局部变量。

但是我们都知道,封装是限制外部的访问,并不是直接拒绝外部的访问,那么我们在函数中私有的属性,怎么才能在外部访问呢?答案就是闭包!

JS中,提供了一种"闭包"的概念:在函数内部,定义一个子函数,可以用子函数访问父函数的私有变量。执行完操作以后,将子函数通过return返回。

代码示例

function func2(){

var num = 1;

function func3(){

var sum = num+10;

alert(sum);

}

return func3;

}

var f = func2();

f();

3、闭包的作用:

① 访问函数的私有变量;

② 让函数的变量始终存在于内存中,而不被释放。

4、闭包的典型应用

我们来做这样一个功能:页面中有6个li,要求实现点击每个li,弹出这个li对应的序号。

HTML代码很简单:

<ul>
            <li>li111111111</li>
            <li>li222222222</li>
            <li>li333333333</li>
            <li>li444444444</li>
            <li>li555555555</li>
            <li>li666666666</li>
    </ul>

那JS代码呢?我觉得很大一部分同学会这样写:

 var lis = document.getElementsByTagName("li");
    for (var i=0;i<lis.length;i++) {
        lis[i].onclick = function(){
            alert("您/点击了第"+i+"个li!");
        }
    } 

那么,这样对吗? 不对!!!我们来分析一下: 页面加载的时候,JS代码会全部执行,也就是上面的for循环在页面加载完就已经执行完了!那,这个i就已经变成了lis.length。 也就是说,你在点击li的时候,无论点击第几个,弹出的都是lis.length。

那么,我们应该怎么修改呢?看代码!

var lis = document.getElementsByTagName("li");
    for (var i=0;i<lis.length;i++) {
        !function(){
            var j = i;
            lis[j].onclick = function(){
                alert("您/点击了第"+j+"个li!");
            }
        }();
    }

区别在哪?明眼人一眼就看穿我们在for循环外面嵌套了一层自执行函数! 这种函数套函数的形式,就形成了闭包!

那作用呢?我们刚才强调,闭包的自执行函数会有自己的作用域。在函数里面的代码没有执行的时候,自执行函数中的j是不会被释放掉的!

也就是说,循环转了6次!生成了6个独立的函数空间,每个空间中有自己独立的j变量,所以最终不会出现所有li点击都是lis.length的情况!

好了!随着继承和闭包的结束!我们JavaScript 面向对象课程的讲解就告一段落了~ 后续的学习过程中有任何疑问,都欢迎大家评论留言哦~

作者:杰瑞教育
出处:http://www.cnblogs.com/jerehedu/ 
版权声明:本文版权归烟台杰瑞教育科技有限公司和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。

技术咨询:

JavaScript面向对象(三)——继承与闭包、JS实现继承的三种方式的更多相关文章

  1. javascript(面向对象,作用域,闭包,设计模式等)

    javascript(面向对象,作用域,闭包,设计模式等) 1. 常用js类定义的方法有哪些? 参考答案:主要有构造函数原型和对象创建两种方法.原型法是通用老方法,对象创建是ES5推荐使用的方法.目前 ...

  2. js 复制文本的四种方式

    js 复制文本的四种方式 一.总结 一句话总结:js文本复制主流方法:document的execCommand方法 二.js 复制文本的四种方式 纯 转载复制,非原创 原地址:http://www.c ...

  3. JS对象创建的几种方式整理

    ​ 本文主要介绍了JS对象创建的几种方式 第一种:Object构造函数创建 var Person = new Object(); Person.name = 'Nike'; Person.age = ...

  4. js 函数定义的2种方式

      js 函数定义的2种方式 CreateTime--2018年3月29日18:36:14 Author:Marydon 方式一: /** * 函数式声明 */ function mode() { c ...

  5. JavaScript面向对象编程(一)原型与继承

    原型(prototype) JavaScript是通过原型(prototype)进行对象之间的继承.当一个对象A继承自另外一个对象B后,A就拥有了B中定义的属性,而B就成为了A的原型.JavaScri ...

  6. 「JavaScript面向对象编程指南」闭包

    闭包 JS只有函数作用域,函数外为全局变量,函数内为局部变量 绿圆是函数fn的作用域,在这范围内可访问局部变量b和全局变量a,橙圆是fn内部函数inner的作用域,此范围内可访问自身作用域内的变量c, ...

  7. Js 类定义的几种方式

    提起面向对象我们就能想到类,对象,封装,继承,多态.在<javaScript高级程序设计>(人民邮电出版社,曹力.张欣译.英文名字是:Professional JavaScript for ...

  8. 为Node.js编写组件的几种方式

    本文主要备忘为Node.js编写组件的三种实现:纯js实现.v8 API实现(同步&异步).借助swig框架实现. 关键字:Node.js.C++.v8.swig.异步.回调. 简介 首先介绍 ...

  9. js动态引入的四种方式

    index.html <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http:// ...

  10. JS定义函数的2种方式以及区别简述(为什么推荐第二种方式)

     无意中看到了阮一峰大神多年前的一篇博客: 12种不宜使用的Javascript语法    看到第9条的时候受到了启发,感觉之前没怎么理解清楚的一些问题好像突然就清晰了,如下图 可能光这样看,有些小伙 ...

随机推荐

  1. Core Graphics框架

    在iOS中常用的框架是Quartz 2D,它是Core Graphics框架的一部分,是一个强大的二维图像绘制引擎.我们日常开发所用到的UIKit的组件都是由Core Graphics框架进行绘制的. ...

  2. Struts2使用小问题-NoSuchFieldException

    五月 12, 2017 4:55:14 下午 com.opensymphony.xwork2.util.logging.jdk.JdkLogger warn 警告: couldn't clear to ...

  3. class对象详解

    我们知道,对于java语言,我们一般先写一个类对象,表示对某一类对象概述,其中包括属性,方法等.我们在对类对象编译时,会产生一个.class对象,jvm在加载类对象时,是加载.class 对象文件,我 ...

  4. DNSmasq服务搭建

    .c { background: #FEFEF2; padding: 30px } hr { border: 1px dotted #70C4EF } DNSmasq介绍 DNSmasq是一个小巧且方 ...

  5. CDH-5.7.1离线安装

    CDH-5.7.1离线安装 参考自:http://blog.csdn.net/jdplus/article/details/45920733   1.文件下载 CDH (Cloudera's Dist ...

  6. 安装sklearn过程

    sklearn是scikit-learn的简称,诸多python工具包都需要这个库 安装顺序: wheel numpy scipy sklearn 因为这个库一直安装不好,都没有动力继续深造机器学习了 ...

  7. Spring Cloud中负载均衡器概览

    在上篇文章中(RestTemplate的逆袭之路,从发送请求到负载均衡)我们完整的分析了RestTemplate的工作过程,在分析的过程中,我们遇到过一个ILoadBalancer接口,这个接口中有一 ...

  8. 【小白成长撸】--多项式求圆周率PI

    /*程序的版权和版本声明部分: *Copyright(c) 2016,电子科技大学本科生 *All rights reserved. *文件名:多项式求PI *程序作用:计算圆周率PI *作者:Amo ...

  9. Android 日夜间切换Demo

    这是其中一种实现模式,也是比较麻烦的一种,首先写布局,不多说上代码 activity_main.xml <?xml version="1.0" encoding=" ...

  10. JavaScript二维码生成——qrcode.js

    在开发中,有时候,我们需要根据不同的内容来动态生成二维码,则可以使用qrcode.js这个小插件来实现. 1.qrcode.js文件内容: (1)未压缩(qrcode.js): /** * @file ...