函数

函数定义和调用

arguments

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

利用arguments,你可以获得调用者传入的所有参数。也就是说,即使函数不定义任何参数,还是可以拿到参数的值。

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

rest参数

ES6标准引入了rest参数,rest参数只能写在最后,前面用...标识,从运行结果可知,传入的参数先绑定a、b,多余的参数以数组形式交给变量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 []

JavaScript自动添加分号带来的陷阱

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

自动添加分号之后变成了。冏rz

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

下面两种写法都没有问题:

function foo() {
    return { name: 'foo' };
}
function foo() {
    return { // 这里不会自动加分号,因为{表示语句尚未结束
        name: 'foo'
    };
}

变量作用域

  • 如果一个变量在函数体内部申明,则该变量的作用域为整个函数体。
  • 由于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的函数定义有个特点,它会先扫描整个函数体的语句,把所有申明的变量“提升”到函数顶部。
  • 提升的只是声明,赋值还是在原来的位置进行赋值。
function foo() {
    var x = 'Hello, ' + y;
    alert(x);
    var y = 'Bob';
}

相当于:

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

所以,我们最好在开头声明所有的变量。

function foo() {
    var
        x = 1, // x初始化为1
        y = x + 1, // y初始化为2
        z, i; // z和i为undefined
}

全局作用域

不在任何函数内定义的变量具有全局作用域。
全局作用域的变量实际上被绑定到window的一个属性。

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

名字空间

全局变量会绑定到window上,不同的JavaScript文件如果使用了相同的全局变量,或者定义了相同名字的顶层函数,都会造成命名冲突,并且很难被发现。
减少冲突的一个方法是把自己的所有变量和函数全部绑定到一个全局变量中。

// 唯一的全局变量MYAPP:
var MYAPP = {};
// 其他变量:
MYAPP.name = 'myapp';
MYAPP.version = 1.0;
// 其他函数:
MYAPP.foo = function () { return 'foo';};

把自己的代码全部放入唯一的名字空间MYAPP
中,会大大减少全局变量冲突的可能。
许多著名的JavaScript库都是这么干的:jQuery,YUI,underscore等等。

局部作用域

由于JavaScript的变量作用域实际上是函数内部,我们在for循环等语句块中是无法定义具有局部作用域的变量的:

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

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

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

常亮

由于var和let申明的是变量,如果要申明一个常量,在ES6之前是不行的,我们通常用全部大写的变量来表示“这是一个常量,不要修改它的值”:
var PI = 3.14;
ES6标准引入了新的关键字const来定义常量,const与let都具有块级作用域:

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

方法

var xiaoming = {
    name: '小明',
    birth: 1990,
    age: function () {
        var y = new Date().getFullYear();
        return y - this.birth;
    }
};

this是一个特殊的变量,始终指向当前对象,也就是xiaoming这个对象。
如果方法是一个对象的一个方法,那么这个方法里面的this指向的是这个对象。但如果这个方法是一个独立的方法,那么这个方法里面的this,在strict模式指向undefined,在非strict模式指向window。为了解决这个问题,我们可以使用apply。

apply

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

//apply的使用
function getAge(){
    var y = new Date().getFullYear();
    return y-this.birth;
}
var xiaoming = {
    name:'小明',
    birth:1991,
    age:getAge
};
console.log(xiaoming.age());//这里直接使用this是有值的
var age = getAge.apply(xiaoming, []);//使用外部的方法就需要用apply了。
console.log(age);

call

call和apply类似,不同之处在于apply是把参数打包成Array传入,而call把参数按照顺序传入。

Math.max(2,3,4);
Math.max.call(null,2,3,4);

利用apply实现装饰器

利用apply(),我们还可以动态改变函数的行为。

JavaScript的所有对象都是动态的,即使内置的函数,我们也可以重新指向新的函数。

现在假定我们想统计一下代码一共调用了多少次parseInt(),可以把所有的调用都找出来,然后手动加上count += 1,不过这样做太傻了。最佳方案是用我们自己的函数替换掉默认的parseInt():

//装饰器
var count = 0;
var oldParseInt = parseInt;
window.parseInt = function(){
    count +=1;
    return oldParseInt.apply(null,arguments);
//  return oldParseInt(arguments);//为什么用这个就不行呢?
}
parseInt('10');
parseInt('10');
parseInt('10');
console.log(parseInt('10'));
console.log(count);

高阶函数

高阶函数就是一个能接收一个函数当做变量的函数。

map/reduce

map

把一个函数作用在Array的每一个元素上。
注意:map对原数组是没有改变的,map返回一个新的数组。

//高阶函数map
function pow(x){
    return x*x;
}
var arr = [1,2,3,4,5];
var arr2 = arr.map(pow);
console.log(arr);
console.log(arr2);
//把数组的所有数字转成字符串
//只需要一行代码,是不是太强大了
arr2 = arr.map(String);
console.log(arr2);

reduce

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

不使用parseInt()把字符串转成int

console.log('不使用parseInt()把字符串转成int');
function string2int(s){
    var arr = [];
    for(var i of s){
        arr.push(i);
    }
    if(s.length==1){
        return s[0]*1;
    }
    var res = arr.reduce(function(x,y){
        return x*10+y*1;    
    });
    return res;
}
// 测试:
if (string2int('0') === 0 && string2int('12345') === 12345 && string2int('12300') === 12300) {
    if (string2int.toString().indexOf('parseInt') !== -1) {
        alert('请勿使用parseInt()!');
    } else if (string2int.toString().indexOf('Number') !== -1) {
        alert('请勿使用Number()!');
    } else {
        alert('测试通过!');
    }
}
else {
    alert('测试失败!');
}

请把用户输入的不规范的英文名字,变为首字母大写,其他小写的规范名字。输入:['adam', 'LISA', 'barT'],输出:['Adam', 'Lisa', 'Bart']。

console.log("请把用户输入的不规范的英文名字,变为首字母大写,其他小写的规范名字。输入:['adam', 'LISA', 'barT'],输出:['Adam', 'Lisa', 'Bart']。");
function normalize(arr) {
    return arr.map(function(x){
        return x.substring(0,1).toUpperCase()+x.substring(1).toLowerCase();
    });
}
// 测试:
if (normalize(['adam', 'LISA', 'barT']).toString() === ['Adam', 'Lisa', 'Bart'].toString()) {
    alert('测试通过!');
}
else {
    alert('测试失败!');
}

一个存在问题的例子:
小明希望利用map()把字符串变成整数,他写的代码很简洁。

'use strict';
var arr = ['1', '2', '3'];
var r;
r = arr.map(parseInt);
alert('[' + r[0] + ', ' + r[1] + ', ' + r[2] + ']');

结果竟然是[1, NaN, NaN],小明百思不得其解,请帮他找到原因并修正代码。
提示:参考Array.prototype.map()的文档
由于map()接收的回调函数可以有3个参数:callback(currentValue, index, array),通常我们仅需要第一个参数,而忽略了传入的后面两个参数。不幸的是,parseInt(string, radix)没有忽略第二个参数,导致实际执行的函数分别是:
parseInt('0', 0); // 0, 按十进制转换
parseInt('1', 1); // NaN, 没有一进制
parseInt('2', 2); // NaN, 按二进制转换不允许出现2
可以改为r = arr.map(Number);,因为Number(value)函数仅接收一个参数。

filter

filter()把传入的函数依次作用于每个元素,然后根据返回值是true还是false决定保留还是丢弃该元素。

//高阶函数filter
var arr = [1,2,3,4,5,6,7,8,9.0];
var res = arr.filter(function(x){
    return x%2==0;
});
console.log(res);

sort(sort直接修改原数组)

Array的排序默认把数字转成字符串然后进行排序(这就导致了10比2小),而且排序是按照Ascii码排序(这导致a比B大)。

//高阶函数-sort
var arr = [10, 20, 1, 2];
arr.sort(function(x,y){
    /*
    if(x<y){
        return -1;
    }else if(x>y){
        return 1;
    }else{
        return 0;
    }
    */
    return y-x;//这样写岂不是更方便
});
console.log(arr);

闭包

箭头函数

generator

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,080评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,422评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,630评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,554评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,662评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,856评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,014评论 3 408
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,752评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,212评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,541评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,687评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,347评论 4 331
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,973评论 3 315
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,777评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,006评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,406评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,576评论 2 349

推荐阅读更多精彩内容

  • 函数就是最基本的一种代码抽象的方式。 定义函数function abs(x) {if (x >=0){return...
    _我和你一样阅读 441评论 0 0
  • 1. 本文是在学习廖雪峰先生的JavaScrip教程 后的归纳 一、JavaScript函数 JavaScript...
    ting723阅读 428评论 0 3
  • 函数函数定义与调用变量作用域全局变量方法高阶函数闭包箭头函数$generator$ 函数 函数定义与调用 定义函数...
    染微言阅读 578评论 0 5
  • 本文是大神廖雪峰的JavaScript教程学习笔记。并不是教程,如有需要,请前往廖雪峰大神大博客. 一、函数定义和...
    0o冻僵的企鹅o0阅读 485评论 1 3
  • 今天是九月10号,离回国整一个月。在这一个月里,我想每天记录打卡,看看最后在墨的这个月我能收获些什么。 今天姨妈大...
    玉蓝田阅读 246评论 0 0