廖大JS教程的笔记

快速入门

数据类型与变量

  1. JavaScript中字符串是以单引号’或双引号”括起来的任意文本

  2. 要特别注意相等运算符==。JavaScript在设计时,有两种比较运算符:

    第一种是==比较,它会自动转换数据类型再比较,很多时候,会得到非常诡异的结果;

    第二种是===比较,它不会自动转换数据类型,如果数据类型不一致,返回false,如果一致,再比较。

    由于JavaScript这个设计缺陷,不要使用==比较,始终坚持使用===比较。

  3. NaN这个特殊的Number与所有其他值都不相等,包括它自己,唯一能判断NaN的方法是通过isNaN()函数。

  4. 浮点数在运算过程中会产生误差,因为计算机无法精确表示无限循环小数。要比较两个浮点数是否相等,只能计算它们之差的绝对值,看是否小于某个阈值。

  5. 变量本身类型不固定的语言称之为动态语言,与之对应的是静态语言。静态语言在定义变量时必须指定变量类型,如果赋值的时候类型不匹配,就会报错。

  6. JavaScript在设计之初,为了方便初学者学习,并不强制要求用var申明变量。这个设计错误带来了严重的后果:如果一个变量没有通过var申明就被使用,那么该变量就自动被申明为全局变量。

    为了修补JavaScript这一严重设计缺陷,ECMA在后续规范中推出了strict模式,在strict模式下运行的JavaScript代码,强制通过var申明变量,未使用var申明变量就使用的,将导致运行错误。启用strict模式的方法是在JavaScript代码的第一行写上:

    ‘use strict’;

    这是一个字符串,不支持strict模式的浏览器会把它当做一个字符串语句执行,支持strict模式的浏览器将开启strict模式运行JavaScript。

    不用var申明的变量会被视为全局变量,为了避免这一缺陷,所有的JavaScript代码都应该使用strict模式。

字符串

  1. 超出范围的索引不会报错,但一律返回undefined。
  2. 需要特别注意的是,字符串是不可变的,如果对字符串的某个索引赋值,不会有任何错误,但是,也没有任何效果
  3. JavaScript为字符串提供了一些常用方法,注意,调用这些方法本身不会改变原有字符串的内容,而是返回一个新字符串。有关字符串的操作,都是保持原型的。在每次操作完会自动生成一个新的变量。

数组

  1. 大多数其他编程语言不允许直接改变数组的大小,越界访问索引会报错。然而,JavaScript的Array却不会有任何错误。在编写代码时,不建议直接修改Array的大小,访问索引时要确保索引不会越界。

  2. concat()方法并没有修改当前Array,而是返回了一个新的Array。

    concat()方法可以接收任意个元素和Array,并且自动把Array拆开,然后全部添加到新的Array里。

  3. concat对添加的数组只进行一次拆分,(1,2,[4,[5,6]])其中5,6不会被拆成单独数组元素

对象

  1. 如果属性名包含特殊字符,就必须用 ’ ’ 括起来。访问该属性也无法使用 . 操作符,必须用[‘xxx’]来访问:
  2. 如果访问一个不存在的属性会返回什么呢?JavaScript规定,访问不存在的属性不报错,而是返回undefined。
  3. 实际上JavaScript对象的所有属性都是字符串,不过属性对应的值可以是任意数据类型。
  4. 如果用in判断对象的一个属性存在与否,注意该属性不一定是对象的,它可能是对象继承得到的。例如,要判断一个属性是否是xiaoming自身拥有的,而不是继承得到的,可以用hasOwnProperty()方法。

条件判断

  1. 在多个if…else…语句中,如果某个条件成立,则后续就不再继续判断了。
  2. JavaScript把null、undefined、0、NaN和空字符串”视为false,其他值一概视为true。

循环

  1. 注意循环判断的边界条件。

Map和Set

  1. JavaScript的默认对象表示方式{}可以视为其他语言中的Map或Dictionary的数据结构,即一组键值对。

    但是JavaScript的对象有个小问题,就是键必须是字符串。但实际上Number或者其他数据类型作为键也是非常合理的。

    为了解决这个问题,最新的ES6规范引入了新的数据类型Map。要测试你的浏览器是否支持ES6规范。

  2. Map和Set是ES6标准新增的数据类型,请根据浏览器的支持情况决定是否要使用。

Iterable

  1. 遍历Array可以采用下标循环,遍历Map和Set就无法使用下标。为了统一集合类型,ES6标准引入了新的iterable类型,Array、Map和Set都属于iterable类型。具有iterable类型的集合可以通过新的for … of循环来遍历。

  2. for … in循环由于历史遗留问题,它遍历的实际上是对象的属性名称。一个Array数组实际上也是一个对象,它的每个元素的索引被视为一个属性。例如当我们手动给Array对象添加了额外的name属性后,for … in循环将带来意想不到的意外效果:

    for … in循环将把name包括在内,但Array的length属性却不包括在内。

  3. for … of循环则完全修复了这些问题,它只循环集合本身的元素。

  4. 然而,更好的方式是直接使用iterable内置的forEach方法,它接收一个函数,每次迭代就自动回调该函数。注意,forEach()方法是ES5.1标准引入的,你需要测试浏览器是否支持。forEach的参数名字是不固定,但是位置是固定的,如果只关心element,那么给forEach一个参数就可以,如果需要index,那么就要给两个参数,如果需要array,就要给三个,也就是这三个参数的含义是定好的。

函数

函数定义和调用

  1. JavaScript还有一个免费赠送的关键字arguments,它只在函数内部起作用,并且永远指向当前函数的调用者传入的所有参数。

  2. 为了获取除了已定义参数a、b之外的参数,我们不得不用arguments,并且循环要从索引2开始以便排除前两个参数,这种写法很别扭,只是为了获得额外的rest参数,有没有更好的方法?

    有,ES6标准引入了rest参数,rest参数只能写在最后,前面用…标识。如果传入的参数连正常定义的参数都没填满,也不要紧,rest参数会接收一个空数组(注意不是undefined)。

  3. 前面我们讲到了JavaScript引擎有一个在行末自动添加分号的机制,这可能让你栽到return语句的一个大坑,如果要把return语句拆成两行,一定要在return尾部紧跟 { ,并在换行语句后加 } 来结束return。

变量作用域

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

  2. JavaScript的函数定义有个特点,它会先扫描整个函数体的语句,把所有申明的变量“提升”到函数顶部,但只提升了变量的声明,但不会提升变量的赋值。

    由于JavaScript的这一怪异的“特性”,我们在函数内部定义变量时,请严格遵守“在函数内部首先申明所有变量”这一规则。最常见的做法是用一个var申明函数内部用到的所有变量

  3. JavaScript默认有一个全局对象window,全局作用域的变量实际上被绑定到window的一个属性。JavaScript实际上只有一个全局作用域。任何变量(函数也视为变量),如果没有在当前函数作用域中找到,就会继续往上查找,最后如果在全局作用域中也没有找到,则报ReferenceError错误。

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

  5. 由于JavaScript的变量作用域实际上是函数内部,我们在for循环等语句块中是无法定义具有局部作用域的变量的。为了解决块级作用域,ES6引入了新的关键字let,用let替代var可以申明一个块级作用域的变量。

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

方法

  1. JavaScript中this的设计有巨大的设计错误,要保证this指向正确,必须用obj.xxx()的形式调用!也可以用var that = this;,你就可以放心地在方法内部定义其他函数,而不是把所有语句都堆到一个方法中。

  2. apply()

    虽然在一个独立的函数调用中,根据是否是strict模式,this指向undefined或window,不过,我们还是可以控制this的指向的!

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

  3. call()

    与apply()类似唯一区别是:apply()把参数打包成Array再传入,call()把参数按顺序传入。

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

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

高阶函数

​ JavaScript的函数其实都指向某个变量。既然变量可以指向函数,函数的参数能接收变量,那么一个函数就可以接收另一个函数作为参数,这种函数就称之为高阶函数。

​ 编写高阶函数,就是让函数的参数能够接收别的函数。

map/reduce

  1. map()作为高阶函数,事实上它把运算规则抽象了,因此,我们不但可以计算简单的f(x)=x^2,还可以计算任意复杂的函数,只需要一行代码。

  2. 特殊情况:

    [‘1’, ‘2’, ‘3’].map(parseInt);

    // While one could expect [1, 2, 3]

    // The actual result is [1, NaN, NaN]

    // parseInt is often used with oneargument, but takes two.

    // The first is an expression and thesecond is the radix.

    // To the callback function,Array.prototype.map passes 3 arguments:

    // the element, the index, the array

    // The third argument is ignored byparseInt, but not the second one,

    // hence the possible confusion. See theblog post for more details

    由于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

  1. 用filter()这个高阶函数,关键在于正确实现一个“筛选”函数。

  2. 注意高阶函数的回调函数的接收参数的数量。

Sort

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

闭包

  1. 我们在外部函数又定义了内部函数,并且,内部函数可以引用外部函数的参数和局部变量,当外部函数返回内部函数时,相关参数和变量都保存在返回的内部函数中,这种称为“闭包(Closure)”的程序结构拥有极大的威力。

    请再注意一点,当我们调用外部函数时,每次调用都会返回一个新的内部函数,即使传入相同的参数。

  2. 各种专业文献上的“闭包”(closure)定义非常抽象,很难看懂。我的理解是,闭包就是能够读取其他函数内部变量的函数。

    由于在Javascript语言中,只有函数内部的子函数才能读取局部变量,因此可以把闭包简单理解成“定义在一个函数内部的函数”。

    所以,在本质上,闭包就是将函数内部和函数外部连接起来的一座桥梁。

  3. 闭包可以用在许多地方。它的最大用处有两个,一个是前面提到的可以读取函数内部的变量,另一个就是让这些变量的值始终保持在内存中。

  4. 另一个需要注意的问题是,返回的函数并没有立刻执行,而是直到调用了该函数才执行。返回闭包时牢记的一点就是:返回函数不要引用任何循环变量,或者后续会发生变化的变量。(以防调用时结果与预期不符)

  5. 通常,一个立即执行的匿名函数可以把函数体拆开,一般这么写:

    (function (x) {
    return x * x;
    })(3);
  6. 闭包就是携带状态的函数,并且它的状态可以完全对外隐藏起来。闭包还可以把多参数的函数变成单参数的函数。

箭头函数

  1. x => x * x

    上面的箭头函数相当于:

    function (x) {
    return x * x;
    }
  2. 箭头函数相当于匿名函数,并且简化了函数定义。箭头函数有两种格式,一种像上面的,只包含一个表达式,连{ … }和return都省略掉了。还有一种可以包含多条语句,这时候就不能省略{ … }和return

  3. 箭头函数看上去是匿名函数的一种简写,但实际上,箭头函数和匿名函数有个明显的区别:箭头函数内部的this是词法作用域,由上下文确定。箭头函数完全修复了this的指向,this总是指向词法作用域,也就是外层调用者obj。

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

Generator

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

  2. generator和函数不同的是,generator由function*定义(注意多出的*号),并且,除了return语句,还可以用yield返回多次。

  3. 直接调用一个generator和调用函数不一样,创建了一个generator对象,还没有去执行它。

    调用generator对象有两个方法,一是不断地调用generator对象的next()方法

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

    当执行到done为true时,这个generator对象就已经全部执行完毕,不要再继续调用next()了。

    第二个方法是直接用for … of循环迭代generator对象,这种方式不需要我们自己判断done

  4. 因为generator可以在执行过程中多次返回,所以它看上去就像一个可以记住执行状态的函数,利用这一点,写一个generator就可以实现需要用面向对象才能实现的功能。

  5. generator还有另一个巨大的好处,就是把异步回调代码变成“同步”代码。这个好处要等到后面学了AJAX以后才能体会到。

标准对象

为了区分对象的类型,我们用typeof操作符获取对象的类型,它总是返回一个字符串。number、string、boolean、function和undefined有别于其他类型。特别注意null的类型是object,Array的类型也是object,如果我们用typeof将无法区分出null、Array和通常意义上的object——{}。

包装对象

  1. number、boolean和string都有包装对象。没错,在JavaScript中,字符串也区分string类型和它的包装类型。包装对象用new创建。

  2. 虽然包装对象看上去和原来的值一模一样,显示出来也是一模一样,但他们的类型已经变为object了!所以,包装对象和原始值用===比较会返回false。

  3. 所以闲的蛋疼也不要使用包装对象!尤其是针对string类型!!!

  4. 如果我们在使用Number、Boolean和String时,没有写new会发生什么情况?

    此时,Number()、Boolean和String()被当做普通函数,把任何类型的数据转换为number、boolean和string类型(注意不是其包装类型)

  5. 总结一下,有这么几条规则需要遵守:

    1. 不要使用newNumber()、new Boolean()、new String()创建包装对象;
    2. 用parseInt()或parseFloat()来转换任意类型到number;
    3. 用String()来转换任意类型到string,或者直接调用某个对象的toString()方法;
    4. 通常不必把任意类型转换为boolean再判断,因为可以直接写 if (myVar) {…};
    5. typeof操作符可以判断出number、boolean、string、function和undefined;
    6. 判断Array要使用Array.isArray(arr);
    7. 判断null请使用myVar ===null;
    8. 判断某个全局变量是否存在用typeof window.myVar === ‘undefined’;
    9. 函数内部判断某个变量是否存在用typeof myVar === ‘undefined’。
  6. 任何对象都有toString()方法吗?null和undefined就没有!确实如此,这两个特殊值要除外,虽然null还伪装成了object类型。

  7. number对象调用toString()报SyntaxError,(123.toString(); //会报语法错误,因为123.的点会当成小数点, 加上括号就可避免此问题)

Date

  1. 时间是浏览器从本机操作系统获取的时间,所以不一定准确,因为用户可以把当前时间设定为任何值。

  2. JavaScript的月份范围用整数表示是0~11,0表示一月,1表示二月……,所以要表示6月,我们传入的是5!这绝对是JavaScript的设计者当时脑抽了一下,但是现在要修复已经不可能了。

  3. Date对象表示的时间总是按浏览器所在时区显示的,不过我们既可以显示本地时间,也可以显示调整后的UTC时间。

  4. 那么在JavaScript中如何进行时区转换呢?实际上,只要我们传递的是一个number类型的时间戳,我们就不用关心时区转换。任何浏览器都可以把一个时间戳正确转换为本地时间。

RegExp(正则表达式)

  1. JavaScript有两种方式创建一个正则表达式:

    第一种方式是直接通过/正则表达式/写出来,第二种方式是通过newRegExp(‘正则表达式’)创建一个RegExp对象。

  2. RegExp对象的test()方法用于测试给定的字符串是否符合条件。

  3. 除了简单地判断是否匹配之外,正则表达式还有提取子串的强大功能。用()表示的就是要提取的分组(Group)。

    如果正则表达式中定义了组,就可以在RegExp对象上用exec()方法提取出子串来。exec()方法在匹配成功后,会返回一个Array,第一个元素是正则表达式匹配到的整个字符串,后面的字符串表示匹配成功的子串。exec()方法在匹配失败时返回null。

  4. JavaScript的正则表达式还有几个特殊的标志,最常用的是g,表示全局匹配。

    全局匹配可以多次执行exec()方法来搜索一个匹配的字符串。当我们指定g标志后,每次运行exec(),正则表达式本身会更新lastIndex属性,表示上次匹配到的最后索引。全局匹配类似搜索,因此不能使用/^…$/,那样只会最多匹配一次。

    正则表达式还可以指定i标志,表示忽略大小写,m标志,表示执行多行匹配。

JSON

​ 详见 JSON笔记

面向对象编程

JavaScript不区分类和实例的概念,而是通过原型(prototype)来实现面向对象编程。

JavaScript的原型链和Java的Class区别就在,它没有“Class”的概念,所有对象都是实例,所谓继承关系不过是把一个对象的原型指向另一个对象而已。

在编写JavaScript代码时,不要直接用obj.proto去改变一个对象的原型,并且,低版本的IE也无法使用proto。Object.create()方法可以传入一个原型对象,并创建一个基于该原型的新对象,但是新对象什么属性都没有.

创建对象

  1. 当我们用obj.xxx访问一个对象的属性时,JavaScript引擎先在当前对象上查找该属性,如果没有找到,就到其原型对象上找,如果还没有找到,就一直上溯到Object.prototype对象,最后,如果还没有找到,就只能返回undefined。

  2. 如果原型链很长,那么访问一个对象的属性就会因为花更多的时间查找而变得更慢,因此要注意不要把原型链搞得太长。

  3. 构造函数是一个普通函数,但是在JavaScript中,可以用关键字new来调用这个函数,并返回一个对象。注意,如果不写new,这就是一个普通函数,它返回undefined。但是,如果写了new,它就变成了一个构造函数,它绑定的this指向新创建的对象,并默认返回this,也就是说,不需要在最后写return this;。

  4. 在strict模式下,this.name= name将报错,因为this绑定为undefined,在非strict模式下,this.name = name不报错,因为this绑定为window,于是无意间创建了全局变量name,并且返回undefined,这个结果更糟糕。

  5. 调用构造函数千万不要忘记写new。为了区分普通函数和构造函数,按照约定,构造函数首字母应当大写,而普通函数首字母应当小写,这样,一些语法检查工具如jslint将可以帮你检测到漏写的new。

原型继承

  1. 我们必须借助一个中间对象来实现正确的原型链,中间对象可以用一个空函数F来实现。
  2. 注意,函数F仅用于桥接,我们仅创建了一个new F()实例,如果把继承这个动作用一个inherits()函数封装起来,还可以隐藏F的定义,并简化代码。
  3. JavaScript的原型继承实现方式就是:

    1. 定义新的构造函数,并在内部用call()调用希望“继承”的构造函数,并绑定this;
    2. 借助中间函数F实现原型链继承,最好通过封装的inherits函数完成;
    3. 继续在新的构造函数的原型上定义新方法。

class继承(ES6引入)

  1. 新的关键字class从ES6开始正式被引入到JavaScript中。class的目的就是让定义类更简单。
  2. 原型继承的中间对象,原型对象的构造函数等等不需要考虑了,可以直接通过extends来实现。
  3. 子类的构造函数可能会与父类不太相同,需要通过super()来调用父类的构造函数,否则父类的相关属性无法正常初始化。
  4. ES6引入的class和原有的JavaScript原型继承没有任何区别,class的作用就是让JavaScript引擎去实现原来需要我们自己编写的原型链代码。
  5. 不是所有的主流浏览器都支持ES6的class。如果一定要现在就用上,就需要一个工具把class代码转换为传统的prototype代码,可以试试Babel这个工具。

浏览器

不同的浏览器对JavaScript支持的差异主要是,有些API的接口不一样,比如AJAX,File接口。对于ES6标准,不同的浏览器对各个特性支持也不一样。

在编写JavaScript的时候,就要充分考虑到浏览器的差异,尽量让同一份JavaScript代码能运行在不同的浏览器中。

浏览器对象

  1. window对象不但充当全局作用域,而且表示浏览器窗口。
  2. navigator对象表示浏览器的信息。请注意,navigator的信息可以很容易地被用户修改,所以JavaScript读取的值不一定是正确的。
  3. screen对象表示屏幕的信息。
  4. location对象表示当前页面的URL信息。
  5. document对象表示当前页面。
  6. history对象保存了浏览器的历史记录,JavaScript可以调用history对象的back()或forward (),相当于用户点击了浏览器的“后退”或“前进”按钮。

    这个对象属于历史遗留对象,对于现代Web页面来说,由于大量使用AJAX和页面交互,简单粗暴地调用history.back()可能会让用户感到非常愤怒。

操作DOM

详见 DOM笔记

操作表单

  1. 用JavaScript操作表单和操作DOM是类似的,因为表单本身也是DOM树。

操作文件

  1. 在HTML表单中,可以上传文件的唯一控件就是。

  2. 注意:当一个表单包含

AJAX

详见 AJAX笔记

CORS

  1. 如果浏览器支持HTML5,那么就可以一劳永逸地使用新的跨域策略:CORS了。

  2. CORS全称Cross-OriginResource Sharing,是HTML5规范定义的如何跨域访问资源。

    PS: 深入了解CORS请查阅W3C文档。

Promise

  1. “承诺将来会执行”的对象在JavaScript中称为Promise对象。

  2. Promise有各种开源实现,在ES6中被统一规范,由浏览器直接支持。

  3. Promise最大的好处是在异步执行的流程中,把执行代码和处理结果的代码清晰地分离了。

  4. 除了串行执行若干异步任务外,Promise还可以并行执行异步任务。有些时候,多个异步任务是为了容错。

  5. 如果我们组合使用Promise,就可以把很多异步任务以并行和串行的方式组合起来执行。

Canvas

  1. Canvas是HTML5新增的组件,它就像一块幕布,可以用JavaScript在上面绘制各种图表、动画等。

  2. 由于浏览器对HTML5标准支持不一致,所以,通常在内部添加一些说明性HTML代码,如果浏览器支持Canvas,它将忽略内部的HTML,如果浏览器不支持Canvas,它将显示内部的HTML。

  3. 在使用Canvas前,用canvas.getContext来测试浏览器是否支持Canvas。

  4. 绘制图像:

    Canvas的坐标以左上角为原点,水平向右为X轴,垂直向下为Y轴,以像素为单位,所以每个点都是非负整数。

  5. 绘制文本:

    就是在指定的位置输出文本,可以设置文本的字体、样式、阴影等,与CSS完全一致

jQuery

详见jQuery笔记

错误处理

JavaScript有一个标准的Error对象表示错误,还有从Error派生的TypeError、ReferenceError等错误对象。我们在处理错误时,可以通过catch(e)捕获的变量e访问错误对象。

JavaScript允许抛出任意对象,包括数字、字符串。但是,最好还是抛出一个Error对象。

处理错误时,请不要简单粗暴地用alert()把错误显示给用户。

错误传播

  1. 如果在一个函数内部发生了错误,它自身没有捕获,错误就会被抛到外层调用函数,如果外层函数也没有捕获,该错误会一直沿着函数调用链向上抛出,直到被JavaScript引擎捕获,代码终止执行。

    所以,我们不必在每一个函数内部捕获错误,只需要在合适的地方来个统一捕获,一网打尽。

异步错误处理

  1. 涉及到异步代码,无法在调用时捕获,原因就是在捕获的当时,回调函数并未执行。类似的,当我们处理一个事件时,在绑定事件的代码处,无法捕获事件处理函数的错误。除了在函数内部捕获错误外,外层代码并无法捕获。

Underscore

完整的函数请参考underscore的文档。

underscore提供了一套完善的函数式编程的接口,让我们更方便地在JavaScript中实现函数式编程。underscore会把自身绑定到唯一的全局变量_上,这也是为啥它的名字叫underscore的原因。

Collections

  1. underscore为集合类对象提供了一致的接口。集合类是指Array和Object,暂不支持Map和Set。

  2. underscore的map()和filter()可以作用于Object。当作用于Object时,传入的函数为function(value, key),第一个参数接收value,第二个参数接收key。对Object作map()操作的返回结果是Array,_.mapObject返回结果是Object。

  3. 当集合的所有元素都满足条件时,_.every()函数返回true,当集合的至少一个元素满足条件时,_.some()函数返回true。当集合是Object时,我们可以同时获得value和key

  4. max / min这两个函数直接返回集合中最大和最小的数,注意,如果集合是Object,max()和min()只作用于value,忽略掉key。(空集合会返回-Infinity和Infinity,所以要先判断集合不为空)

  5. groupBy()把集合的元素按照key归类,key由传入的函数返回,非常方便。

  6. shuffle()用洗牌算法随机打乱一个集合,sample()则是随机选择一个或多个元素。

Arrays

  1. first / last顾名思义,这两个函数分别取第一个和最后一个元素。

  2. flatten()接收一个Array,无论这个Array里面嵌套了多少个Array,flatten()最后都把它们变成一个一维数组。

  3. zip()把两个或多个数组的所有元素按索引对齐,然后按索引合并成新数组。例如,你有一个Array保存了名字,另一个Array保存了分数,现在,要把名字和分数给对上,用zip()轻松实现。unzip()则是反过来。js的数组可以越界访问,所以必须注意两个合并数组是不是索引对齐

  4. object()把两个或多个数组的所有元素按索引对齐,然后按索引合并成新Object。注意_.object()是一个函数,不是JavaScript的Object对象。js的数组可以越界访问,所以必须注意两个合并数组是不是索引对齐

  5. range()让你快速生成一个序列,不再需要用for循环实现了。

Functions

  1. bind()可以帮我们绑定指针。

  2. partial()就是为一个函数创建偏函数。如果不想固定第一个参数,用_作占位符,固定住第二个参数。创建偏函数的目的是将原函数的某些参数固定住,可以降低新函数调用的难度。

  3. memoize()可以自动缓存函数计算结果。

  4. once()保证某个函数执行且仅执行一次。

  5. delay()可以让一个函数延迟执行,效果和setTimeout()是一样的,但是代码明显简单了。如果要延迟调用的函数有参数,把参数也传进去

Objects

  1. keys()可以非常方便地返回一个object自身所有的key,但不包含从原型链继承下来的。allKeys()除了object自身的key,还包含从原型链继承下来的。

  2. 和keys()类似,values()返回object自身但不包含原型链继承的所有值,注意,没有allValues(),原因未知。

  3. mapObject()就是针对object的map版本。

  4. invert()把object的每个key-value来个交换,key变成value,value变成key。

  5. extend()把多个object的key-value合并到第一个object并返回,如果有相同的key,后面的object的value将覆盖前面的object的value。extendOwn()和extend()类似,但获取属性时忽略从原型链继承下来的属性。

  6. 如果我们要复制一个object对象,就可以用clone()方法,它会把原有对象的所有属性都复制到新的对象中。注意,clone()是“浅复制”。所谓“浅复制”就是说,两个对象相同的key所引用的value其实是同一对象。

  7. isEqual()对两个object进行深度比较,如果内容完全相同,则返回true。isEqual()对Array也可以比较。

Chaining

  1. underscore提供了把对象包装成能进行链式调用的方法,就是chain()函数。但因为每一步返回的都是包装对象,所以最后一步的结果需要调用value()获得最终结果。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值