JavaScript全面学习(函数)

1.定义函数的两种方法:

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

或者

var abs = function (x) {
    if (x >= 0) {
        return x;
    } else {
        return -x;
    }
};  //记得要加分号,因为这是赋值abs变量

2.调用函数

abs(10, 'blablabla'); // 返回10
abs(-9, 10, 'hehe', null); // 返回9,不受多个参数的影响,因为第一个参数就return了
abs(); // x为undefined,返回NaN

3.关键字arguments,类似于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最常用于判断传入参数的个数:

if (arguments.length === 0) //如果参数个数为0...

 

4.用rest获取所有参数:

function foo(a, b) {
    var i, rest = [];
    if (arguments.length > 2) {
        for (i = 2; i<arguments.length; i++) {
            rest.push(arguments[i]);   // 在rest数组后添加元素
        }
    }
    console.log('a = ' + a);
    console.log('b = ' + b);
    console.log(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前面有3个点,如果没有的话,相当于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
// 3

foo(1);
// 结果:
// a = 1
// b = undefined
// undefined

5.接受任意个参数并返回它们的和:

function sum(...rest) {
  var sum = 0;
  for(var i of rest) {
    sum += i;      
  }
  return sum;
}

6.JavaScript在行末自动添加分号

7.不同函数内部的同名变量互相独立,互不影响

8.内部函数可以访问外部函数定义的变量,反过来则不行:

function foo() {
    var x = 1;
    function bar() {
        var y = x + 1; // bar可以访问foo的变量x!
    }
    var z = y + 1; // ReferenceError! foo不可以访问bar的变量y!
}

9.JavaScript会先扫描整个函数体的语句,把所有申明的变量“提升”到函数顶部,但不会提升变量的赋值:

'use strict';

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

foo();  //结果显示Hello, undefined,说明y没有值,但是已经声明了

10.JavaScript默认有一个全局对象window,全局作用域的变量实际上被绑定到window的一个属性:

'use strict';

var course = 'Learn JavaScript';
alert(course); // 'Learn JavaScript'
alert(window.course); // 'Learn JavaScript'  与上面等效

11.let解决块级作用域

'use strict';

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

function foo() {
    var sum = 0;
    for (let i=0; i<100; i++) {
        sum += i;
    }
    i += 1; // SyntaxError,无法引用i
}

12.关键字const来定义常量,constlet都具有块级作用域:

'use strict';

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

13.this指的是被调用函数的当前对象  ,多层内嵌会使指示错误,可以在函数开头用var that = this;来捕捉固定this

    要指定函数的this指向哪个对象,可以用函数本身的apply方法,它接收两个参数,第一个参数就是需要绑定的this变量,第二个参数是Array,表示函数本身的参数。

function getAge() {
    var y = new Date().getFullYear();
    return y - this.birth;
}

var xiaoming = {
    name: '小明',
    birth: 1990,
    age: getAge
};

xiaoming.age(); // 25
getAge.apply(xiaoming, []); // 25, this指向xiaoming, 参数为空

对普通函数调用,我们通常把this绑定为null

另一个与apply()类似的方法是call(),唯一区别是:

  • apply()把参数打包成Array再传入;

  • call()把参数直接按顺序传入。 

14.map函数用法,()里调用其他函数

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] 令数组的所有元素都执行()里的函数

还能这样

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

 15.reduce函数用法:必须接收两个参数,reduce()把结果继续和序列的下一个元素做累积计算,例如求和:

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

其他例子

var arr = [1, 3, 5, 7, 9];
arr.reduce(function (x, y) {
    return x * 10 + y;   // 把return值当成下一个x,与下一个y循环函数
}); // 13579

16.箭头函数

function (x) {
    return x * x;
}                 //相当于箭头函数x => x * x

 如果包含多条语句,这时候就不能省略{ ... }return

如果参数不是一个,就需要用括号()括起来:

// 两个参数:
(x, y) => x * x + y * y

// 无参数:
() => 3.14

// 可变参数:
(x, y, ...rest) => {
    var i, sum = x + y;
    for (i=0; i<rest.length; i++) {
        sum += rest[i];
    }
    return sum;
}

如果要返回一个对象(单表达式),要外加一个括号,写成:

x => ({ foo: x })

回顾前面的例子,

var obj = {
    birth: 1990,
    getAge: function () {
        var b = this.birth; // 1990
        var fn = () => new Date().getFullYear() - this.birth; // this指向obj对象
        return fn();
    }
};
obj.getAge(); // 25

箭头函数使this总是指向词法作用域,也就是外层调用者obj,如果是之前的直接创建函数,this就会指向window

由于this在箭头函数中已经按照词法作用域绑定了,所以,用call()或者apply()调用箭头函数时,无法对this进行绑定,即传入的第一个参数被忽略

var obj = {
    birth: 1990,
    getAge: function (year) {
        var b = this.birth; // 1990
        var fn = (y) => y - this.birth; // this.birth仍是1990
        return fn.call({birth:2000}, year);   //箭头函数里的this已经绑定原对象obj,
                                                          //  此时的this.birth仍然是1990,
                                                     //  而不会将this绑定到传入的 birth:2000
    }
};
obj.getAge(2015); // 25

 

17.split() 方法用于把一个字符串分割成字符串数组,如果要把字符串数组变成“数”组,要乘以1,可以如下写

var arr=string.split('').map(x => x * 1)

18.parseInt()函数可解析一个字符串,并返回一个整数。

    语法:parseInt(string, radix);

    string 必需。要被解析的字符串。

    radix 可选。表示要解析的数字的基数。该值介于 2 ~ 36 之间。

    如果省略该参数或其值为 0,则数字将以 10 为基础来解析。

    如果它以 “0x” 或 “0X” 开头,将以 16 为基数。

    如果该参数小于 2 或者大于 36,则 parseInt() 将返回 NaN。

parseInt("10");          //返回 10         10进制
parseInt("19",10);       //返回 19 (10+9)  10进制
parseInt("11",2);        //返回 3 (2+1)    2进制
parseInt("17",8);        //返回 15 (8+7)   8进制
parseInt("1f",16);        //返回 31 (16+15)    16进制
parseInt("010");         //未定:返回 10 或 8

19.filter方法

var arr = [1, 2, 4, 5, 6, 9, 10, 15];
var r = arr.filter(function (x) {   
    return x % 2 !== 0;    //把arr中的偶数过滤掉,留下x%2!==0的
});
r; // [1, 5, 9, 15]
var arr = ['A', '', 'B', null, undefined, 'C', '  '];
var r = arr.filter(function (x) {
    return x && x.trim(); // 把false的空字符串过滤掉
});                            //注意:IE9以下的版本没有trim()方法
arr; // ['A', 'B', 'C']
var arr = ['A', 'B', 'C'];
var r = arr.filter(function (element, index, self) {  // 可以输入3个参数
    console.log(element); // 依次打印'A', 'B', 'C'
    console.log(index); // 依次打印0, 1, 2
    console.log(self); // self就是变量arr   会输出3次Array [ "A", "B", "C" ]
    return true;
});
var r = arr.filter(function (element, index, self) {
    return self.indexOf(element) === index;
});  //过滤掉arr中重复的元素   indexOf() 方法返回某个指定的字符串值在字符串中首次出现的位置,注意是首次

选出数组里面的素数:

 function get_primes(arr) {
      var i;
      var res = arr.filter(function(x){
            if(x<2) return false;     //排除掉1
            for(i=2; i*i<=x; i++){     
                if(x%i === 0){return false;}   //从2开始往上面的数一个个地除,直到确认是不是素数
                }
            return true;   //是素数就return true。
        });
        return res;
}

 20.对于两个元素xy,如果认为x < y,则返回-1,如果认为x == y,则返回0,如果认为x > y,则返回1

21.闭包(不大懂意义):返回一个函数,不立即执行

function lazy_sum(arr) {
    var sum = function () {
        return arr.reduce(function (x, y) {
            return x + y;
        });
    }
    return sum;
}
var f = lazy_sum([1, 2, 3, 4, 5]); // function sum()   
                       //调用lazy_sum()时,返回的并不是求和结果,而是返回求和函数
f(); // 15       //调用函数f时,才是真正计算求和的结果

当我们调用lazy_sum()时,每次调用都会返回一个新的函数,即使传入相同的参数(每次调用的结果互不影响):

var f1 = lazy_sum([1, 2, 3, 4, 5]);
var f2 = lazy_sum([1, 2, 3, 4, 5]);
f1 === f2; // false

返回的函数并没有立刻执行,而是直到调用了f()才执行:

function count() {
    var arr = [];
    for (var i=1; i<=3; i++) {     // 这里正常思维是最终i=4,但是没想到4最后居然进入循环里面运算了??最终return 4*4
        arr.push(function () {
            return i * i;
        });
    }
    return arr;
}

var results = count();
var f1 = results[0];
var f2 = results[1];
var f3 = results[2];
f1(); // 16
f2(); // 16
f3(); // 16   等到3个函数都返回时,它们所引用的变量i已经变成了4

正确写法:

function count() {
    var arr = [];
    for (var i=1; i<=3; i++) {
        arr.push((function (n) {     // 在里面再创建一个函数,用该函数的参数绑定循环变量当前的值
            return function () {
                return n * n;
            }
        })(i));              // 这样是用i去立即执行函数 注意括号的括法,(function (x) { return x * x }) (i);
    }
    return arr;
}

var results = count();
var f1 = results[0];
var f2 = results[1];
var f3 = results[2];

f1(); // 1
f2(); // 4
f3(); // 9

借助闭包,可以封装一个私有变量。例如用JavaScript创建一个计数器:

'use strict';
function create_counter(initial) {
    var x = initial || 0;   //在没有初始值initial的时候给一个初始值
    return {
        inc: function () {
            x += 1;
            return x;
        }
    }
}
var c1 = create_counter();
c1.inc(); // 1
c1.inc(); // 2
c1.inc(); // 3
var c2 = create_counter(10);
c2.inc(); // 11
c2.inc(); // 12
c2.inc(); // 13

换句话说,闭包就是携带状态的函数,并且它的状态可以完全对外隐藏起来:

function make_pow(n) {
    return function (x) {
        return Math.pow(x, n);   // 计算x的n次方
    }
}
// 创建两个新函数:
var pow2 = make_pow(2);
var pow3 = make_pow(3);
pow2(5); // 25             相当于Math.pow(5, 2)
pow3(7); // 343            相当于Math.pow(7, 3)

只需要用函数,就可以用计算机实现运算,而不需要0123这些数字和+-*/这些符号:

'use strict';

// 定义数字0:
var zero = function (f) {
    return function (x) {
        return x;
    }
};

// 定义数字1:
var one = function (f) {
    return function (x) {
        return f(x);
    }
};

// 定义加法:
function add(n, m) {
    return function (f) {
        return function (x) {
            return m(f)(n(f)(x));
        }
    }
}
// 计算数字2 = 1 + 1:
var two = add(one, one);

// 计算数字3 = 1 + 2:
var three = add(one, two);

// 计算数字5 = 2 + 3:
var five = add(two, three);

// 给3传一个函数,会打印3次:
(three(function () {
    console.log('print 3 times');
}))();

// 给5传一个函数,会打印5次:
(five(function () {
    console.log('print 5 times');
}))();

22.(意义不明啊!)generator(生成器)是ES6标准引入的新的数据类型。一个generator看上去像一个函数,但可以返回多次。

    编写一个产生斐波那契数列的函数:

function* fib(max) {    //注意多出的*号
    var
        t,
        a = 0,
        b = 1,
        n = 1;
    while (n < max) {
        yield a;
        t = a + b;
        a = b;
        b = t;
        n ++;
    }
    return a;
}
fib(5); // fib {[[GeneratorStatus]]: "suspended", [[GeneratorReceiver]]: Window}
        // fib(5)仅仅是创建了一个generator对象,还没有去执行它

//可以这样调用:
var f = fib(5); f.next(); // {value: 0, done: false} f.next(); // {value: 1, done: false} f.next(); // {value: 1, done: false} f.next(); // {value: 2, done: false} f.next(); // {value: 3, done: true}

next()方法每次遇到yield x;就返回一个对象{value: x, done: true/false},然后“暂停”。返回的value就是yield的返回值,done表示这个generator是否已经执行结束了。

一般这样调用:

for (var x of fib(6)) {   // 注意要6,坑啊
    console.log(x); // 依次输出0, 1, 1, 2, 3
}

下面是天书:

用generator的话,AJAX可以大大简化代码:

try {
    r1 = yield ajax('http://url-1', data1);
    r2 = yield ajax('http://url-2', data2);
    r3 = yield ajax('http://url-3', data3);
    success(r3);
}
catch (err) {
    handle(err);
}

用一个对象来保存状态:

var fib = {
    a: 0,
    b: 1,
    n: 0,
    max: 5,
    next: function () {
        var
            r = this.a,
            t = this.a + this.b;
        this.a = this.b;
        this.b = t;
        if (this.n < this.max) {
            this.n ++;
            return r;
        } else {
            return undefined;
        }
    }
};

自增,并保存数字:

'use strict';
function* next_id() {
var i=1;
while(true)
{
  yield i++;
}
}

 

posted @ 2016-12-11 18:40  森森森shen  阅读(535)  评论(0编辑  收藏  举报