• js自学笔记(3)


    1.函数定义

    在JavaScript中,定义函数的方式如下:

    function abs(x) {
        if (x >= 0) {
            return x;
        } else {
            return -x;
        }
    }
    如果没有return语句,函数执行完毕后也会返回结果,只是结果为undefined
    var abs = function (x) {
        if (x >= 0) {
            return x;
        } else {
            return -x;
        }
    };
    上述两种定义完全等价,注意第二种方式按照完整语法需要在函数体末尾加一个;,表示赋值语句结束。

    2.参数

    JavaScript允许传入任意个参数而不影响调用,因此传入的参数比定义的参数多或者少也没有问题

       要避免收到undefined,可以对参数进行检查:

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

      JavaScript还有一个关键字arguments,它只在函数内部起作用,并且永远指向当前函数的调用者传入的所有参数arguments类似Array但它不是一个Array

    function foo(x) {
        alert(x); // 10
        for (var i=0; i<arguments.length; i++) {
            alert(arguments[i]); // 10, 20, 30
        }
    }
    foo(10, 20, 30);

    实际上arguments最常用于判断传入参数的个数。你可能会看到这样的写法:

    // foo(a[, b], c)
    // 接收2~3个参数,b是可选参数,如果只传2个参数,b默认为null:
    function foo(a, b, c) {
        if (arguments.length === 2) {
            // 实际拿到的参数是a和b,c为undefined
            c = b; // 把b赋给c
            b = null; // b变为默认值
        }
        // ...
    }
    为了获取除了已定义参数ab之外的参数,我们可以用rest参数
    function foo(a, b, ...rest) {
        console.log('a = ' + a);
        console.log('b = ' + b);
        console.log(rest);
    }
    foo(1, 2, 3, 4, 5);
    // 结果:
    // a = 1
    // b = 2
    // Array [ 3, 4, 5 ]
    
    foo(1);
    // 结果:
    // a = 1
    // b = undefined
    // Array []
    

    rest参数只能写在最后,前面用...标识,从运行结果可知,传入的参数先绑定ab多余的参数以数组形式交给变量rest,所以,不再需要arguments我们就获取了全部参数。

    如果传入的参数连正常定义的参数都没填满,也不要紧,rest参数会接收一个空数组(注意不是undefined)。

    3.return

    JavaScript引擎有一个在行末自动添加分号的机制,这可能让你栽到return语句的一个大坑:

    function foo() {
        return { name: 'foo' };
    }
    foo(); // { name: 'foo' }
    

    如果把return语句拆成两行:

    function foo() {
        return
            { name: 'foo' };
    }
    foo(); // undefined
    

    要小心了,由于JavaScript引擎在行末自动添加分号的机制,上面的代码实际上变成了:

    function foo() {
        return; // 自动添加了分号,相当于return undefined;
            { name: 'foo' }; // 这行语句已经没法执行到了
    }
    

    所以正确的多行写法是:

    function foo() {
        return { // 这里不会自动加分号,因为{表示语句尚未结束
            name: 'foo'
        };
    }
    4.
    由于JavaScript的函数可以嵌套,此时,内部函数可以访问外部函数定义的变量,反过来则不行:
    'use strict';
    
    function foo() {
        var x = 1;
        function bar() {
            var y = x + 1; // bar可以访问foo的变量x!
        }
        var z = y + 1; // ReferenceError! foo不可以访问bar的变量y!
    }

    JavaScript的函数在查找变量时从自身函数定义开始,从“内”向“外”查找。
    如果内部函数定义了与外部函数重名的变量,则内部函数的变量将“屏蔽”外部函数的变量
    5.变量提升:

    JavaScript的函数定义有个特点,它会先扫描整个函数体的语句,把所有申明的变量“提升”到函数顶部

    'use strict';
    
    function foo() {
        var x = 'Hello, ' + y;
        alert(x);
        var y = 'Bob';
    }
    
    foo();
    

    虽然是strict模式,但语句var x = 'Hello, ' + y;并不报错,原因是变量y在稍后申明了。但是alert显示Hello, undefined,说明变量y的值为undefined。这正是因为JavaScript引擎自动提升了变量y的声明,但不会提升变量y的赋值。

    对于上述foo()函数,JavaScript引擎看到的代码相当于:

    function foo() {
        var y; // 提升变量y的申明
        var x = 'Hello, ' + y;
        alert(x);
        y = 'Bob';
    }
    

    由于JavaScript的这一怪异的“特性”,我们在函数内部定义变量时,请严格遵守“在函数内部首先申明所有变量”这一规则。

    最常见的做法是用一个var申明函数内部用到的所有变量:

    function foo() {
        var
            x = 1, // x初始化为1
            y = x + 1, // y初始化为2
            z, i; // z和i为undefined
        // 其他语句:
        for (i=0; i<100; i++) {
            ...
        }
    }
    6.

    全局作用域

    不在任何函数内定义的变量就具有全局作用域。实际上,JavaScript默认有一个全局对象window,全局作用域的变量实际上被绑定到window的一个属性:

    'use strict';
    
    var course = 'Learn JavaScript';
    alert(course); // 'Learn JavaScript'
    alert(window.course); // 'Learn JavaScript'
    

    因此,直接访问全局变量course和访问window.course是完全一样的。

    你可能猜到了,由于函数定义有两种方式,以变量方式var foo = function () {}定义的函数实际上也是一个全局变量,因此,顶层函数的定义也被视为一个全局变量,并绑定到window对象

    'use strict';
    
    function foo() {
        alert('foo');
    }
    
    foo(); // 直接调用foo()
    window.foo(); // 通过window.foo()调用

    7.

    我们在for循环等语句块中是无法定义具有局部作用域的变量的:

    'use strict';
    
    function foo() {
        for (var i=0; i<100; i++) {
            //
        }
        i += 100; // 仍然可以引用变量i
    }
    

    为了解决块级作用域,ES6引入了新的关键字letlet替代var可以申明一个块级作用域的变量

    'use strict';
    
    function foo() {
        var sum = 0;
        for (let i=0; i<100; i++) {
            sum += i;
        }
        i += 1; // SyntaxError
    }

    ES6标准引入了新的关键字const来定义常量,constlet都具有块级作用域

    'use strict';
    
    const PI = 3.14;
    PI = 3; // 某些浏览器不报错,但是无效果!
    PI; // 3.14

    8.


    在一个对象中绑定函数,称为这个对象的方法。

    如果我们给xiaoming绑定一个函数,就可以做更多的事情。比如,写个age()方法,返回xiaoming的年龄:

    var xiaoming = {
        name: '小明',
        birth: 1990,
        age: function () {
            var y = new Date().getFullYear();
            return y - this.birth;
        }
    };
    
    xiaoming.age; // function xiaoming.age()
    xiaoming.age(); // 今年调用是25,明年调用就变成26了


    function getAge() {
        var y = new Date().getFullYear();
        return y - this.birth;
    }
    
    var xiaoming = {
        name: '小明',
        birth: 1990,
        age: getAge
    };
    
    xiaoming.age(); // 25, 正常结果
    getAge(); // NaN
    

    单独调用函数getAge()怎么返回了NaN请注意,我们已经进入到了JavaScript的一个大坑里。

    JavaScript的函数内部如果调用了this,那么这个this到底指向谁?

    答案是,视情况而定!

    如果以对象的方法形式调用,比如xiaoming.age(),该函数的this指向被调用的对象,也就是xiaoming,这是符合我们预期的。

    如果单独调用函数,比如getAge(),此时,该函数的this指向全局对象,也就是window

     
     9.

    一个函数接收另一个函数作为参数,这种函数就称之为高阶函数

    一个最简单的高阶函数:

    function add(x, y, f) {
        return f(x) + f(y);
    }
    

    当我们调用add(-5, 6, Math.abs)时,参数xyf分别接收-56和函数Math.abs。

    10.map

    由于map()方法定义在JavaScript的Array中,我们调用Arraymap()方法,传入我们自己的函数,

    就得到了一个新的Array作为结果:

    function pow(x) {
        return x * x;
    }
    
    var arr = [1, 2, 3, 4, 5, 6, 7, 8, 9];
    arr.map(pow); // [1, 4, 9, 16, 25, 36, 49, 64, 81]
    

    map()传入的参数是pow,即函数对象本身。

    我们能一眼看明白“把pow(x)作用在Array的每一个元素并把结果生成一个新的Array”。

    所以,map()作为高阶函数,事实上它把运算规则抽象了,因此,我们不但可以计算简单的f(x)=x2,还可以计算任意复杂的函数,

    比如,把Array的所有数字转为字符串:

    var arr = [1, 2, 3, 4, 5, 6, 7, 8, 9];
    arr.map(String); // ['1', '2', '3', '4', '5', '6', '7', '8', '9']
    

    只需要一行代码。

    11.reduce

    Array的reduce()把一个函数作用在这个Array[x1, x2, x3...]上,这个函数必须接收两个参数reduce()把结果继续和序列的下一个元素做累积计算,其效果就是:

    [x1, x2, x3, x4].reduce(f) = f(f(f(x1, x2), x3), x4)
    

    比方说对一个Array求和,就可以用reduce实现:

    var arr = [1, 3, 5, 7, 9];
    arr.reduce(function (x, y) {
        return x + y;
    }); // 25

    12.filter

    filter也是一个常用的操作,它用于把Array的某些元素过滤掉,然后返回剩下的元素

    
    

    map()类似,Arrayfilter()也接收一个函数。和map()不同的是,filter()把传入的函数依次作用于每个元素,

    然后根据返回值是true还是false决定保留还是丢弃该元素

    
    

    在一个Array中,删掉偶数,只保留奇数,可以这么写:

    
    
    var arr = [1, 2, 4, 5, 6, 9, 10, 15];
    var r = arr.filter(function (x) {
        return x % 2 !== 0;
    });
    r; // [1, 5, 9, 15]

    通常我们仅使用第一个参数,表示Array的某个元素。

    回调函数还可以接收另外两个参数,表示元素的位置和数组本身:

    var arr = ['A', 'B', 'C'];
    var r = arr.filter(function (element, index, self) {
        console.log(element); // 依次打印'A', 'B', 'C'
        console.log(index); // 依次打印0, 1, 2
        console.log(self); // self就是变量arr
        return true;
    });
    
    

    利用filter,可以巧妙地去除Array的重复元素:

    'use strict';
    
    var
        r,
        arr = ['apple', 'strawberry', 'banana', 'pear', 'apple', 'orange', 'orange', 'strawberry'];
    

     

    r = arr.filter(function (element, index, self) {
    return self.indexOf(element) === index;
    });

    去除重复元素依靠的是indexOf总是返回第一个元素的位置,后续的重复元素位置与indexOf返回的位置不相等,因此被filter滤掉了。

    13.sort

    Arraysort()方法默认把所有元素先转换为String再排序,结果'10'排在了'2'的前面

    要按数字大小排序,我们可以这么写:

    var arr = [10, 20, 1, 2];
    arr.sort(function (x, y) {
        if (x < y) {
            return -1;//x<y则返回-1表示不用sort(从小到大)
        }
        if (x > y) {
            return 1;
        }
        return 0;
    }); // [1, 2, 10, 20]
    

    如果要倒序排序,我们可以把大的数放前面:

    var arr = [10, 20, 1, 2];
    arr.sort(function (x, y) {
        if (x < y) {
            return 1;
        }
        if (x > y) {
            return -1;
        }
        return 0;
    }); // [20, 10, 2, 1]
    

    默认情况下,对字符串排序,是按照ASCII的大小比较的,现在,我们提出排序应该忽略大小写,按照字母序排序。

    要实现这个算法,不必对现有代码大加改动,只要我们能定义出忽略大小写的比较算法就可以:

    var arr = ['Google', 'apple', 'Microsoft'];
    arr.sort(function (s1, s2) {
        x1 = s1.toUpperCase();
        x2 = s2.toUpperCase();
        if (x1 < x2) {
            return -1;
        }
        if (x1 > x2) {
            return 1;
        }
        return 0;
    }); // ['apple', 'Google', 'Microsoft']
    

    忽略大小写来比较两个字符串,实际上就是先把字符串都变成大写(或者都变成小写),再比较。

    sort()方法会直接对Array进行修改,它返回的结果仍是当前Array

     



     
  • 相关阅读:
    03_HibernateSessionFactory源码分析
    02_ThreadLocal语法与源码分析
    01_Java 软、弱引用语法介绍
    Xpath定位和CssSelector定位的区别
    Mac 上自动化构建 jenkins 操作步骤(中)git环境搭建
    Mac 上自动化构建 jenkins 操作步骤(上)
    UI自动化常用的几种等待方法
    Selenium-webdriver 之元素定位方法归类
    统一初始化(Uniform Initialization)
    Mac下electron编译
  • 原文地址:https://www.cnblogs.com/EganZhang/p/6515612.html
Copyright © 2020-2023  润新知