天天看点

【JavaScript精华荟萃】精华汇总+易混淆知识(中篇)

文章目录

  • 一、原型继承
  • 二、Class关键字
  • 1.ES6之前
  • 2.ES6后
  • 3.补充
  • 三、Map和Set
  • 四、Iterator(迭代器)
  • 1.遍历数组
  • 2.遍历Map
  • 3.遍历Set
  • 五、JS中不完善的报错+手动改进(throw+【argument】+补充:【...rest】)
  • 1.函数参数中报错不完善
  • 2.var局部作用域中的bug
  • 六、使JS的原生方法失效+恢复
  • 七、多个js文件全局遍历冲突问题
  • 八、方法与函数

一、原型继承

实例中,tom本身没有run方法,无法调用run

【JavaScript精华荟萃】精华汇总+易混淆知识(中篇)

但如果想调用,则可让tom的原型对象指向Student,这样,tom对象在调用run方法时,在本对象中查不到run方法时,则会去tom对象的__proto__属性中去查找run方法,也就是先在tom对象的原型链上查找紧邻上一个原型对象中的方法

【JavaScript精华荟萃】精华汇总+易混淆知识(中篇)

当然,要注意的是,将tom的原型指向Student时,Student的原型也会连接过来

【JavaScript精华荟萃】精华汇总+易混淆知识(中篇)

每个对象和原型都有原型,对象的原型指向原型对象,而父的原型又指向父的父,这种原型层层连接起来的就构成了原型链。

作为一个对象,当你访问其中的一个属性或方法的时候,如果这个对象中没有这个方法或属性,那么JavaScript引擎将会访问这个对象的__proto__属性所指向的上一个对象,并在那个对象中查找指定的方法或属性,如果不能找到,那就会继续通过那个对象 的__proto__属性指向的对象进行向上查找,直到这个链表结束

二、Class关键字

1.ES6之前

在ES6之前,只能这样定义类

<script>
    "use strict";

    function Student(name) {
        this.name = name;
    }

    //给Student类新增一个方法,上面的Student(name)为构造函数
    //给类增加方法,因此需指向Student(name)函数的原型对象
    Student.prototype.hello = function () {
        console.log('Hello ' + this.name);
    }
</script>      
【JavaScript精华荟萃】精华汇总+易混淆知识(中篇)
【JavaScript精华荟萃】精华汇总+易混淆知识(中篇)

引入一张图

【JavaScript精华荟萃】精华汇总+易混淆知识(中篇)

2.ES6后

在ES6时,引入了Class关键字,使得此操作更加方便、合理和规范

<script>
    "use strict";

    class Student {
        constructor(name) {
            this.name = name;
        }
        
        hello() {
            console.log('Hello ' + this.name)
        }
    }
</script>      
【JavaScript精华荟萃】精华汇总+易混淆知识(中篇)

继承用关键字extends

<script>
    "use strict";

    class Student {
        constructor(name) {
            this.name = name;
        }

        hello() {
            console.log('Hello ' + this.name)
        }
    }

    class PrimaryStudent extends Student {
        constructor(name, grade) {
            super(name);
            this.grade = grade;
        }

        say() {
            console.log('I‘m a primary student');
        }
    }
</script>      

如图,extends本质也是原型链的思想

(注:下图Object原型后面还有连接的原型)

【JavaScript精华荟萃】精华汇总+易混淆知识(中篇)
【JavaScript精华荟萃】精华汇总+易混淆知识(中篇)

3.补充

【JavaScript精华荟萃】精华汇总+易混淆知识(中篇)
【JavaScript精华荟萃】精华汇总+易混淆知识(中篇)

(1)得到原型类:

方法一:对象实例名._proto_

方法二:类名.prototype

三、Map和Set

二者为ES6的新增数据类型。

Map:(键值对)

常用方法:

  • set(‘key’, value):添加某个值。
  • delete(value):删除某个值。
  • get(‘key’):得到此键的某个值
【JavaScript精华荟萃】精华汇总+易混淆知识(中篇)
【JavaScript精华荟萃】精华汇总+易混淆知识(中篇)

Set:(去重的无序集合)

常用方法:

  • add(value):添加某个值。
  • delete(value):删除某个值。
  • has(value):某值是否在集合内。
  • clear():清除集合中所有元素。

四、Iterator(迭代器)

为ES6新特性。

1.遍历数组

普通的遍历方法如下:

for in:遍历下标

for of:遍历值

【JavaScript精华荟萃】精华汇总+易混淆知识(中篇)

2.遍历Map

for of:遍历键+值

【JavaScript精华荟萃】精华汇总+易混淆知识(中篇)

3.遍历Set

for of:遍历值

【JavaScript精华荟萃】精华汇总+易混淆知识(中篇)

五、JS中不完善的报错+手动改进(throw+【argument】+补充:【…rest】)

1.函数参数中报错不完善

例子:

<script>
    "use strict";

    function abs(x) {
        if (x >= 0) {
            return x;
        } else {
            return -x;
        }
    }
</script>      

函数传递多个参数,或不传递参数,或是传递另一种类型的参数,都不会报错,最多是NaN值,从而可能导致间接报错,但不会出现直接报错

【JavaScript精华荟萃】精华汇总+易混淆知识(中篇)

加一段判断参数类型的语句,来手动抛出异常(但也只能解决只传一个参数时的类型问题)

<script>
    "use strict";

    function abs(x) {
        if (typeof x !== 'number') {
            throw 'Not a Number~';
        }
        if (x >= 0) {
            return x;
        } else {
            return -x;
        }
    }
</script>      
【JavaScript精华荟萃】精华汇总+易混淆知识(中篇)

由于可利用JS的关键字arguments来接收多个函数参数,否则,若函数只定义了一个参数,则其它传入的参数(从第二个参数往后的所有参数),都不会参与到函数中,但却保留在arguments中

<script>
    "use strict";

    function abs(x) {
        if (typeof x !== 'number') {
            throw 'Not a Number~';
        }

        console.log('x=>' + x);
        console.log('argument遍历');
        for (let i = 0; i < arguments.length; i++) {
            console.log(arguments[i]);
        }

        if (x >= 0) {
            return x;
        } else {
            return -x;
        }
    }
</script>      
【JavaScript精华荟萃】精华汇总+易混淆知识(中篇)

因此,再次改进代码,即可写出一个报错完整的函数

<script>
    "use strict";

    function abs(x) {
        if (typeof x !== 'number') {
            throw 'Not a Number~';
        }

        if (arguments.length > 1) {
            throw 'Please Input one arg~'
        }

        if (x >= 0) {
            return x;
        } else {
            return -x;
        }
    }
</script>      
【JavaScript精华荟萃】精华汇总+易混淆知识(中篇)

补充:另外,还有一个…rest(ES6新增),写在函数定义的参数列表最后面,用于接收除已定义参数外的额外传入的参数

<script>
    "use strict";

    function talk(x, ...rest) {
        let i;

        for (i = 0; i < arguments.length; i++) {
            console.log(arguments[i]);
        }
        console.log('-----------------------')
        console.log(rest);
    }
</script>      
【JavaScript精华荟萃】精华汇总+易混淆知识(中篇)

2.var局部作用域中的bug

<script>
    "use strict";

    for (var i = 0; i < 2; i++) {
        console.log('in: ' + i);
    }
    //仍然可以输出i
    console.log('out: ' + i);
</script>      
【JavaScript精华荟萃】精华汇总+易混淆知识(中篇)

改进:将var改为let

<script>
    "use strict";

    for (let i = 0; i < 2; i++) {
        console.log('in: ' + i);
    }
    //不可以输出i
    console.log('out: ' + i);
</script>      
【JavaScript精华荟萃】精华汇总+易混淆知识(中篇)

六、使JS的原生方法失效+恢复

预先记录原方法引用,随后消除原方法:

<script>
    "use strict";

    let x = 'xxx';

    var old_alert = window.alert;

    window.alert = function (){

    }

    window.alert(123);
    window.old_alert('old: ' + x);
</script>      
【JavaScript精华荟萃】精华汇总+易混淆知识(中篇)

当然,也可以设置恢复

<script>
    "use strict";

    let x = 'xxx';

    var old_alert = window.alert;

    window.alert = function (){

    }

    window.alert(123);
    // window.old_alert('old: ' + x);

    //恢复
    window.alert = old_alert;
    window.old_alert('re-old: ' + x);
</script>      
【JavaScript精华荟萃】精华汇总+易混淆知识(中篇)

七、多个js文件全局遍历冲突问题

问题:由于我们所有的全局变量都会绑定到window对象上,因此,如果使用不同的js文件,遇到相同名字的全局变量时,就会产生冲突。

解决方法:在每个js中都自定义一个唯一的全局变量对象,将这个脚本文件中的所有全局变量都赋予该全局变量对象,从而降低全局命名冲突的问题。(JQuery也参考了这个思想)

例如:

<script>
    "use strict";

    //唯一全局变量
    var appT = {};

    appT.name = 'zlc';
    appT.add = function(x, y) {
        return x + y;
    }

    //使用时
    console.log(window.appT.add(1, 2));
</script>      

八、方法与函数

(在讲述的时候需格外注意,不要将方法说成函数)

方法:

【JavaScript精华荟萃】精华汇总+易混淆知识(中篇)

或是这样的形式

【JavaScript精华荟萃】精华汇总+易混淆知识(中篇)

方法调用:obj.method(),需要加括号

函数:

【JavaScript精华荟萃】精华汇总+易混淆知识(中篇)

用函数的apply方法调用函数:格式:函数名.apply(对象, 参数列表);

【JavaScript精华荟萃】精华汇总+易混淆知识(中篇)

普通函数调用和apply函数调用,结果相同

【JavaScript精华荟萃】精华汇总+易混淆知识(中篇)

直接调用getAge()函数将会报错

【JavaScript精华荟萃】精华汇总+易混淆知识(中篇)

继续阅读