JavaScript笔记简介

TIP

最后更新时间:2019年09月21日

字数:61741

宝剑锋从磨砺出,梅花香自苦寒来。

参考资料

JavaScript

三大组成部分

JavaScript并不是只有编程语言es部分,还包括浏览器和html的部分

  • ECMAScript
  • DOM
  • BOM

ECMAScript

  • ECMA (欧洲计算机制造商协会)
  • 1997年,在ECMA(欧洲计算机制造商协会)的协调下,由Netscape、Sun、微软、Borland组成的工作组确定统一标准:ECMA-262,可以理解为ECMAscript
  • ECMAscript由此诞生

TIP

我们常用的JavaScript语法都是ES5

  • ECMAScript 5 (ES5)
    • ECMAScript第五个版本(第四版因为过于复杂废弃了)

BOM - 浏览器对象模型

  • Browser Object Model
  • 指浏览器对象模型,它使JavaScript可以和浏览器进行交
  • 主要包含内容:
    • navigator对象:浏览器对象,通过这个对象可以判定用户所使用的浏览器,包含了浏览器相关信息

    • screen对象:屏幕对象,可以获取一些和屏幕相关的信息

    • history对象:浏览历史对象,包含了用户对当前页面的浏览历史,但我们无法查看具体的地址,可以简单的用来前进或后退一个页面

    • location对象:用于获取浏览器的当前的网页地址,还可以重定向新的地址

    • 弹出框相关方法

    • 计时相关方法

    • 等等

DOM - 文档对象模型

  • Document Object Model
  • 指文档对象模型,使用它可以访问HTML文档中的所有元素
  • 主要包含内容:
    • DOM树:DOM规定HTML文档中的每一个元素都是一种节点
    • 查找文档
    • 节点操作
    • CSS属性操作
    • 事件event
    • 等等

JavaScript注释

一些要求比较严格的公司,注释是有规范的!一定要写注释!

单行注释

<!-- html文件注释 -->

// 双斜线是单行注释
1
2
3

多行注释

/* 多行注释 */
/*
	我是
	多行
	注释
*/
1
2
3
4
5
6

文档注释

  • 文档注释一般用于对一些方法做说明
  • 文档注释借助工具可以直接生成项目文档
/**
* 登录方法
* @param username {string} 用户名
* @param password {number} 密码
* @returns {boolean} 登录是否成功
*/
  function login(username,password) {
	// 返回登录是否成功
  }
1
2
3
4
5
6
7
8
9

JavaScript变量

变量其实就相当于我们数学中的未知数x,y,z等

JavaScript标识符

  • 凡是我们自主命名名称的都可以认为是标识符
  • 比如:变量名称,函数名称,对象属性名等

标识符命名规范

  • 不能以数字开头
  • 区分大小写
  • 第一个字符可以是字母、数字以及美元符号($)和下划线(_)
    • js底层底层保存标识符时实际上是采用的Unicode编码
    • 理论上讲,所有的utf-8中含有的内容都可以作为标识符
  • 二个字符及后面的字符,还可以用数字
  • JavaScript有一些保留字,不能用作标识符
  • Infinity、NaN、undefined虽然不是保留字,也不允许做标识符

行业通用命名规范

  • 变量命名要有意义,避免无意义的变量命名,如:a,b,c,d等
  • 遵守驼峰命名法首字母小写,然后驼峰形式
    • userName,userInfo,userId等

TIP

中文是合法的标识符,可以用作变量名,语法上是可以通过的,只不过开发约束规范不允许

建议大家准时行业通用命名规范,这样写出来的代码比较优雅,同时便于多人协作开发

常见JavaScript保留字

arguments、breakcasecatchclassconstcontinuedebuggerdefaultdeletedoelseenum、eval、
exportextendsfalsefinallyforfunctionifimplementsimportininstanceofinterfaceletnewnullpackageprivateprotectedpublicreturnstaticsuperswitchthisthrowtruetrytypeofvarvoidwhilewithyield
1
2
3
4
5
6
7
8
9
10
11

作用域

作用域就是一套规则,用于确定在何处以及如何查找变量(标识符)的规则

全局变量和全局作用域

  • 在任何地方都可以访问的变量,就是全局变量
  • 全局变量生效的作用域,就是全局作用域
var a = "全局";
function fn(){
    console.log(a);
}
fn();//结果:全局
1
2
3
4
5

局部变量和局部作用域

  • 只能在函数内部访问的变量就是局部变量
  • 对应的作用域就是局部作用域(又称函数作用域)
  • 因为js只有函数可以划分作用域(当前阶段ES5)
function fn(){
    var a = "局部";
}
fn();
console.log(a);// ReferenceError: a is not defined

function fn1() {
    b = '不带var'
}
fn1()
console.log(b); // 不带var
1
2
3
4
5
6
7
8
9
10
11

TIP

函数内部声明变量的时候,一定要使用var命令。如果不用的话,你实际上声明了一个全局变量!

块级作用域

  • ES6引入了块级作用域,明确允许在块级作用域中声明函数
  • 块级作用域中,函数声明语句的行为类似于let,在块级作用域之外,不可以引用

块级作用域规则

  • 块级作用域用一对花括号来表示
{
	// 这里是块级作用域
}

{{ // 块级作用域可以嵌套 }}
1
2
3
4
5
  • ES5规定,函数只能在顶层作用域和函数作用域之中声明,不能在块作用域中声明
  • ES6引入了块级作用域,明确允许在块级作用域中声明函数
    • 块级作用域中,函数声明语句的行为类似于let,在块级作用域之外,不可以引用
  • 问题在于:浏览器可以不完全遵守这个规定,可以有自己的解析方式,当然在严格模式下回报错
  • 举例:
    • 允许在块级作用域内声明函数
    • 函数声明类似于var,即会提升到全局作用域或函数作用域的头部
    • 同时,函数声明还会提升到所在块级作用域的头部。
    • 上面的3个规则只对ES6 的浏览器实现有效,其他环境的实现不用遵守,还是将块级作用域当做let处理

TIP

  • 考虑到环境导致的行为差异太大,应该避免在块级作用域内声明函数
  • 如果确实需要,也应该写成函数表达式
  • ES6的块级作用域允许声明函数的规则,只在使用大括号的情况下成立,没有使用大括号,会报错

词法作用域

词法作用域是作用域的一种工作模型

  • 作用域共有两种主要的工作模型:
    • 词法作用域(JavaScript采用,又称静态作用域)
    • 动态作用域

静态作用域(词法作用域)

  • 词法作用域是由你在写代码时将变量和块作用域写在哪里来决定的
  • 词法作用域的创建发生在预编译阶段,因为词法阶段属于预编译的一个过程

TIP

编译阶段就能够知道全部标识符在哪里以及是如何声明的,所以词法作用域是静态的作用域,也就是词法作用域能够预测在执行代码的过程中如何查找标识符

动态作用域

  • 在函数调用时才决定作用域

作用域欺骗

  • eval()和with可以通过其特殊性用来“欺骗”词法作用域,不过正常情况下都不建议使用,会产生性能问题

作用域链

  • 全局作用域和局部作用域中变量的访问权限,其实是由作用域链决定的

  • 每次进入一个新的执行环境,都会创建一个用于搜索变量和函数的作用域链。作用域链是函数被创建的作用域中对象的集合。

  • 作用域链可以保证对执行环境有权访问的所有变量和函数的有序访问

  • 用域链的最前端始终是当前执行的代码所在环境的变量对象(如果该环境是函数,则将其活动对象作为变量对象),下一个变量对象来自包含环境(包含当前还行环境的环境),下一个变量对象来自包含环境的包含环境,依次往上,直到全局执行环境的变量对象。全局执行环境的变量对象始终是作用域链中的最后一个对象

  • 标识符解析是沿着作用域一级一级的向上搜索标识符的过程。搜索过程始终是从作用域的前端逐地向后回溯,直到找到标识符(找不到,就会导致错误发生)

TIP

JavaScript的作用域在被执行之前已经创建,日后再去执行时只需要按照作用域链去寻找即可

执行环境

  • 执行环境定义了变量和函数有权访问的其他数据,决定了他们各自的行为。

  • 每个执行环境都有与之对应的变量对象(variable object),保存着该环境中定义的所有变量和函数。

  • 我们无法通过代码来访问变量对象,但是解析器在处理数据时会在后台使用到它。

  • 执行环境有全局执行环境(也称全局环境)和函数执行环境之分。

  • 执行环境如其名是在运行和执行代码的时候才存在的,所以我们运行浏览器的时候会创建全局的执行环境,在调用函数时,会创建函数执行环境

全局执行环境

  • 全局执行环境是最外围的一个执行环境
  • 在web浏览器中,我们可以认为他是window对象,因此所有的全局变量和函数都是作为window对象的属性和方法创建的。
  • 代码载入浏览器时,全局环境被创建,关闭网页或者关闭浏览时全局环境被销毁。

函数执行环境

  • 每个函数都有自己的执行环境
  • 当执行流进入一个函数时,函数的环境就被推入一个环境栈中,当函数执行完毕后,栈将其环境弹出,把控制权返回给之前的执行环境

执行环境结论

TIP

  • 函数的局部环境可以访问函数作用域中的变量,也可以访问和操作父环境(包含环境)乃至全局环境中的变量
  • 父环境只能访问其包含环境和自己环境中的变量和函数,不能访问其子环境中的变量和函数
  • 全局环境只能访问全局环境中的变量和函数,不能直接访问局部环境中的任何数据

作用域链总结(重点)

  • 执行环境决定了变量的生命周期,以及哪部分代码可以访问其中变量
  • 执行环境有全局执行环境(全局环境)和局部执行环境之分
  • 每次进入一个新的执行环境,都会创建一个用于搜索变量和函数的作用域链
  • 函数的局部环境可以访问函数作用域中的变量和函数,也可以访问其父环境,乃至全局环境中的变量和环境
  • 全局环境只能访问全局环境中定义的变量和函数,不能直接访问局部环境中的任何数据
  • 变量的执行环境有助于确定应该合适释放内存

提升(hoisting)

JavaScript中提升有两种,一种是变量提升,另一种是函数提升

变量提升

  • 把变量提升到函数的顶部,需要注意的是,变量提升只是提升变量的声明,不会吧变量的值也提升上来!
  • 也就是:只提升变量声明,不提升变量赋值

函数提升

  • 把函数提升到前面(仅限函数声明形式才能被提升
  • 函数创建三种方法:
    • 函数声明(静态的),比如 function fn () {}
    • 函数表达式(函数字面量),比如:var fn = function(){}
    • 函数构造法(动态的,匿名的,Function()构造函数)

JavaScript闭包

var f1 = function(){
    var a=1;
    return function f2(){
        a++;
        alert(a)
    }
}
var b = f1();
b();            // 2
b();            // 3
1
2
3
4
5
6
7
8
9
10

b便是闭包f2的函数

闭包需求

有些情况下,我们需要在函数外部访问函数内部的局部变量,但是这个时候我们是访问不到的,因为内部的局部变量被GC回收掉了

垃圾回收GC

GC参考文档 GC五分钟入门

什么是闭包

JavaScript权威指南

  • JavaScript函数是将要执行的代码以及执行这些代码作用域构成的一个综合体。在计算机学术语里,这种代码和作用域额综合体叫做闭包,言外之意所有的JavaScript函数都是闭包

JavaScript高级程序设计(第三版)

  • 具体描述在第7章函数表达式第7.2节(页码为第178页)
  • 闭包是指有权访问另一个函数作用域中的变量的函数。创建闭包的常见方式,就是在一个函数内部创建另一个函数

个人理解

  • 只要存在调用内部函数的可能,JavaScript就需要保留被引用的函数。而且JavaScript运行时需要跟踪引用这个内部函数的所有变量,直到最后一个变量废弃,JavaScript的垃圾收集器才能释放相应的内存空间
  • 个人赞同:闭包是指有权限访问另一个函数作用域的变量的函数,创建闭包的常见方式就是在一个函数内部创建另一个函数

阮一峰大神

  • 阮一峰:学习JavaScript闭包
  • 闭包就是能够读取其他函数内部变量的函数”。并且认为:“可以把闭包简单理解成‘定义在一个函数内部的函数“

闭包的特性

  • 函数嵌套函数
  • 函数内部可以引用函数外部的参数和变量
  • 参数和变量不会被垃圾回收机制回收

闭包解决的问题

  • 可以读取函数内部的变量
  • 让这些变量的值始终保持在内存中,可以防止GC回收
    • 在内存中维持一个变量,可以做缓存(但使用多了同时也是一项缺点,消耗内存)
  • 利用闭包的特性,可以写一些匿名函数自调用
    • 保护函数内的变量安全 ,实现封装,防止变量流入其他环境发生命名冲突
    • 匿名自执行函数可以减少内存消耗

闭包的注意事项

  • 被引用的私有变量不能被销毁,增大了内存消耗,造成内存泄漏
  • 闭包会在父函数外部,改变父函数内部变量的值

闭包优化

  • 在一些变量使用完之后,使用null销毁它

程序设计语言中的变量

  • 在程序设计语言中,变量可分为自由变量与约束变量两种。

自由变量(free variable)

  • 在计算机程序设计中,术语自由变量(free variable)是指函数中使用的变量,它们既不是函数的局部变量也不是参数。在当前语境下,术语非局部变量(non-local variable)是它的同义词
var a=10;
function fn(){
    console.log(a);//这个时候a就是自由变量了
}
1
2
3
4
  • 当自由变量所属的函数被定义时,自由变量的值就已经确定,是该函数定义处的父作用域中同名变量的值。
  • 在上述示例中,函数内部的a是自由变量,它的值是fn的父作用域中a的值,也就是10

TIP

自由变量的作用域在程序运行前就已经确定!

它在外层作用域中声明,但在内层作用域中使用

自由变量取值

var a = 111

function fn1 () {
    console.log(a)
}

function fn2 (f) {

    var a = 222;

    (function(){
        f()  // 这里是111,而不是222
    })()


}

fn2(fn1)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
  • 要到创建这个函数的那个作用域中取值
  • 是“创建”,而不是“调用”,也就是我们所谓的“静态作用域”

约束变量(bound variable)

  • 约束变量在某个状态之前是自由的,在某个状态之后它会被绑定到一个或一组具体的值。

TIP

简单来说,局部变量和参数都被认为是约束变量;而不是约束变量的则是自由变量

《JavaScript函数式编程》 Michael Fogus

TIP

自由变量与闭包的关系是,自由变量闭合于闭包的创建。

闭包背后的基本原理是,如果一个函数包含内部函数,那么它们都可以看到其中声明的变量;这些变量被称为‘自由’变量。

然而,这些变量可以被内部函数捕获,从高阶函数中return实现‘越狱’,以供以后使用。

唯一需要注意的是,捕获函数必须在外部函数内定义。函数内没有任何局部声明之前(既不是被传入,也不是局部声明)使用的变量就是被捕获的变量

名称绑定(name binding)

  • 在计算机程序语言中,名称绑定就是将实体(entities- data and/code)与标识符(identifiers)相关联的过程。
  • 我们将一个标识符绑定到一个对象上叫作引用(reference)该对象。
  • 机器语言没有内置的标识符记法(notion),但是名称与对象(name-object)绑定做为一个服务和记法(notation)被程序语言实现并且被程序员所使用。
  • 绑定与作用域的概念紧密相关,因为作用域从词法上,在计算机编码中定义了何名称在何种可能执行的路径下于何处绑定何具体对象。

结论

引用来源

总而言之,关于JavaScript闭包我做出以下理解(限于计算机程序设计语境下):

简单理解(基本)

闭包是一个函数

细化(来自阮一峰博客)

闭包是定义在一个函数内部,并且能够读取其他函数内部变量的函数

明确(来自《JavaScript高级程序设计》第三版)

闭包是定义在一个外部函数内部,并且能够访问(存取)外部函数中自由变量的函数

广义抽象(来自Mozilla与维基百科)

闭包是一个抽象的环境记录,它包含狭义的闭包函数以及在创建该函数时,每个自由变量及其值或名称绑定存储区直接的一个映射。

JavaScript代码执行过程简单介绍

JavaScript代码执行分为两个阶段

  • 代码的检查装载阶段(预编译阶段)
  • 代码的执行阶段

预编译阶段

第一步:读取分析整个源代码

第二步:扫描函数声明和变量声明

  • 处理函数声明:如果出现函数命名冲突,会进行覆盖
  • 处理变量声明:如果出现变量命名冲突,会忽略

第三步:将扫描到的函数和变量保存到一个对象中

  • 普通的保存到一个对象中
  • 全局的保存到window对象中
  • 变量的值是undefined,函数的值则指向该函数(是一个函数字符串)
var f = 999; // 用var定义的变量,赋值过
var a = 1;   // 用var定义的变量,赋值过
var b;       // 用var定义的变量,没有赋值
c = 2;       // 默认不使用var声明的遍历,是全局变量,会构建在window中

// 用声明的方式声明的函数
function fn(n){
  alert(f);
}

// 函数表达式
var d = function(){
    console.log('我是d')
}
fn();

// 构建的结果
{f : undefined,a : undefined, c : undefined, fn : 'function(){alert(f);var f = 999;}'}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18

预处理阶段注意事项

  • 不用var声明的全局变量是不在预处理构建结果里面的

  • 预处理的函数必须是JS中用声明的方式声明的函数(不是函数表达式)

console.log(fn);
var fn = 1;
function fn(){
    console.log('我是fn');
}
// 结果:fn(){ console.log('我是fn');}

console.log(fn);
function fn(){
    console.log('我是fn');
}
var fn = 1;
// 结果:fn(){ console.log('我是fn');}

console.log(fn);
var fn = 1;
var fn = 2;
// 结果:undefined

console.log(fn);
function fn(){
    console.log('我是fn');
}
function fn(){
    console.log('我是后面的fn');
}
// 结果:fn(){ console.log('我是后面的fn');}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27

TIP

  • JavaScript在预处理的时候会优先将函数保存到window对象中,然后如果发现同名的是变量,这时它会忽略这个变量,如果发现同名的是函数,那么后面的会将前面的覆盖

  • 可以理解为:在既有函数声明又有变量声明的时候,他们就像CSS中的权重那样,函数声明的权重总是高一些,所以最终结果往往是指向函数声明的引用

执行阶段

  • 代码执行阶段是建立在预编译基础上的,执行阶段很多东西可以用词法结构来解释
console.log(a); // undefined
// console.log(b); //这里会报错,因为不会被提前声明,找不到
console.log(c); // c(){console.log('c');}
console.log(d); // undefined
var a = 1;
b = 2;
console.log(b); // 2
function c(num1,num2){
    console.log('c');
}

var d = function(){
    console.log('d');
}
console.log(d); //  (){console.log('d');}
c(123); // 只传一个值的情况
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
  • 全局预处理情况
{
    a:undefined
    没有b
    c:对函数的一个引用
    d:undefined
}
1
2
3
4
5
6
  • var a = 1 执行完之后
{
    a:1
    没有b
    c:对函数的一个引用
    d:undefined
}
1
2
3
4
5
6
  • b=2 执行完之后
{
    a:1
    b:2
    c:对函数的一个引用
    d:undefined
}
1
2
3
4
5
6
  • 全部执行完之后
{
    a:1
    b:2
    c:指向函数
    d:指向函数
}
1
2
3
4
5
6
  • 最后调用c,只传递一个值
c 函数内部,词法结构
{
	num1:123,
	num2:undefined
}
1
2
3
4
5
  • 函数内部声明变量和参数重名的情况
function f(num1,num2,num3){

    console.log(num1); // funciton num1(){}
    function num1(){

    }

    var num2
    console.log(num2) // 100

    console.log(num3) // 222
    var num3 = 111
    console.log(num3) // 111

}

f(88,100,222); 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
  • 参数num1
    • JavaScript在预处理的时候,将参数放到词法对象中,然后发现里面有一个同名的函数,这时JavaScript就会把这个函数放到词法对象中,并覆盖之前的参数,而它的值指向的就是这个函数
    • 当我们程序真正运行时,我们把参数传过去,JavaScript在这里并不会把 num1 这个参数值赋值给这个形参
  • 参数num2
    • JavaScript预处理发现有变量重名声明,会忽略
    • 所以打印的是num2还是传递进来的100
  • 参数num3
    • 第一次打印222,是用的传递进来的,虽然num3被重复声明,但是被忽略了
    • 第二次打印相当于重新赋值了,打印的是重新赋值后的值111

数据类型

  • JavaScript数据类型

原始类型(基本类型)

  • 按值访问,可以操作保存在变量中实际的值。
  • null和undefined比较特殊
  • 基本类型的数据是没有属性和方法的,引用类型才会有
  • ECMAScript 有 5 种原始类型(primitive type),即 Undefined、Null、Boolean、Number 和 String

引用类型

  • 引用类型通常叫做类(class),也就是说,遇到引用值,所处理的就是对象
  • 引用类型的值是保存在内存中的对象
  • 基本类型的数据是存放在栈内存中的,而引用类型的数据是存放在堆内存中的
  • 引用类型定义了一个对象其实是在栈内存中存储了一个指针,这个指针指向堆内存中该对象的存储地址。

TIP

  • 与其他语言不同的是,JavaScript不允许直接访问内存中的位置,也就是说不能直接操作对象的内存空间(C语言可以)。
  • 在操作对象时,实际上是在操作对象的引用而不是实际的对象。所以引用类型的值是按引用访问的。

基本包装类型

  • String
  • Boolean
  • Number

基本包装类型说明

  • 把一个基本类型尝试用对象的方式使用它的时候,比如访问length属性,或者增加一些属性的操作时,javascript会把这些基本类型转化为对应的包装类型对象。
  • 完成这样一个访问比如a.length返回以后了以后,这个临时对象会被销毁掉。
var str = 'abc'

// 这里可以访问length 说明是对象
console.log(str.length) //3

str.length = 'zs'

// 这里undefined 其实是因为length访问结束之后,这个临时对象会被销毁掉了
console.log(str.name) // undefined
1
2
3
4
5
6
7
8
9

Null

  • null就是表示没有东西
  • Null类型是只有一个值的数据类型,这个特殊的值是null
  • null值表示一个指向不存在或无效的对象或地址(空指针)
  • null用来表示尚未存在的对象,常用来表示函数企图返回一个不存在的对象

typeof null

  • typeof null 返回object
  • 这是JavaScript的设计错误,它应该被示为null类型,而不是object类型,或者非对象类型
  • null 本身是基本类型,也有人说null是一种特殊的object

场景

  • Object.prototype._proto_的值也是null
  • 在JS的DOM元素获取中,如果没有获取到指定的元素对象,结果一般是null
  • 否定null值返回true,但将其与false(或true)进行比较则会给出false
  • 在基础数学运算中,null值将被转换为0
  • 在正则捕获的时候,如果没有捕获到结果,默认也是null
console.log(typeof null) // objetc
console.log(document.getElementById('1')) // null
console.log(!null) // true
console.log( null == true) // false
console.log( null == false) // false
1
2
3
4
5

TIP

  • undefined是从null派生出来的 所以undefined==null true
  • 声明一个空对象应该将其赋值为null

undefined

  • 它也是JavaScript的原始数据类型
  • undefined是全局作用域的一个变量
  • undefined的最初值就是原始数据类型undefined

出现场景

  • 在变量提升(预解析)阶段
  • 变量声明但没有初始化时,只声明未定义
  • 要查询的对象属性或数组的元素不存在时
  • 如果函数没有任何返回值,则返回undefined
  • 引用没有提供实参的函数形参的值也只会得到undefined
  • 在JS的严格模式下(”use strict”),没有明确的主体,this指的就是undefined
  • 否定undefined值返回true,但将其与false(或true)进行比较则会给出false
var a ;
console.log(a) // undefined

var b = {}
console.log(b.name) // undefined

var arr = ['hello','xiaofengge'] 
console.log(arr[3]) // undefined
console.log(typeof undefined) // undefined
console.log(!undefined) // true
console.log( undefined == true) // false
console.log( undefined == false) // false
1
2
3
4
5
6
7
8
9
10
11
12

null vs undefined

相同点

  • 它们都是“假值”,当被否定时,两者的值都是true,也就是取反都是true
  • 它们两个都不包含任何属性和方法
  • 都只有一个值:
    • Undefined类型只有一个值,即undefined
    • Null类型也只有一个值,即null
  • 都是原始类型的值,保存在栈中变量本地
  • 在根据需要转换成对象时两者都会报异常,即throws TypeError

不同点

  • null是JavaScript语言的关键字,而undefined是JavaScript预定义的全局变量,不是关键字

  • 类型不一样

    • typeOf undefined => undefined
    • typeOf null => object
  • 转化为值时不一样:

    • undefined为NaN
    • null为0
  • 转化为字符串:

    • undefined在根据需要自行转换为字符串是转换为"undefined"
    • null转换为"null"
  • undefined有自己的数据类型(undefined),null只是一个对象

// 类型
console.log(typeof null) // object
console.log(typeof undefined) // undefined

// 转化为数值
console.log(Number(null)) // 0
console.log(Number(undefined)) // NaN

// 会执行类型转换
console.log(undefined == null) // true 
// 首先计算其操作数的值,然后比较这两个值,比较过程没有任何类型转换
console.log(undefined === null) // false 
1
2
3
4
5
6
7
8
9
10
11
12

TIP

  • undefined是表示系统级的、出乎意料的或类似错误的值的空缺
  • 而null是表示程序级的、正常的或在意料之中的值的空缺
  • 如果想把它们赋值给变量或属性或者当做参数传入函数,最好选择使用null

Number

  • Number 对象是原始数值的包装对象
  • typeof Number() === 'number'
  • Number转换数据失败的话会变成NaN(not a number)

进制

一般我们在JavaScript中操作的数据是:2进制,8进制,10进制,16进制等

二进制

  • 二进制是计算技术中广泛采用的一种数制。
  • 二进制数据是用0和1两个数码来表示的数。
  • 它的基数为2,进位规则是逢二进一,借位规则是借一当二
// 得到一个2进制的数

var num = 11
// 11转化为二进制
var ret = num.toString(2)
console.log(ret) // 1011
1
2
3
4
5
6

八进制

  • 如果前缀为 0, JavaScript 会把数值常量解释为八进制数
  • 比如:0123(八进制)和123(正常的十进制)是不一样的
  • 它的基数为8,进位规则是逢八进一,借位规则是借一当八
  • 八进制有效值是:0-7
  • 超出范围:
    • 前面的0会被忽略
    • 后续位数以十进制显示
// 012是一个八进制的数字,转化为八进制还是12
var num = 012
console.log(num.toString(8)) // 12

// 12是一个十进制的数字,转化为八进制是14
var num1 = 12
console.log(num1.toString(8)) // 14

// 超出范围
var num2 = 08
console.log(parseInt(num2))  // 8  结论:0被忽略,后续位数以十进制显示
1
2
3
4
5
6
7
8
9
10
11

十进制

  • 除非特殊情况,我们一般使用的都是10进制

十六进制

  • 如果前缀为 0x, JavaScript 会把数值常量解释为十六进制数
  • 它的基数为16,进位规则是逢十六进一,借位规则是借一当十六
  • 十六进制:0-9和a或A(10),b或B(11),c或C(12),d或D(),e或E(14),f或F(15)
  • a-f和A-F是一样的,十六进制不区分大小写
// 十进制转16进制
var num = 9
console.log(num.toString(16)) // 9

var num1 = 10
console.log(num1.toString(16)) // a

var num2 = 11
console.log(num2.toString(16)) // b

var num3 = 12
console.log(num3.toString(16)) // c

var num4 = 13
console.log(num4.toString(16)) // d

var num5 = 14
console.log(num5.toString(16)) // e

var num6 = 15
console.log(num6.toString(16)) // f

var num7 = 16
console.log(num7.toString(16)) // 10

var num8 = 1269325090287
console.log(num8.toString(16)) // 12789abcdef

var num9 = 0x12789ABCDEF
console.log(num9.toString(10)) // 1269325090287

// 十六进制不区分大小写
console.log(0xabcdef === 0xABCDEF) // true
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33

TIP

绝不要在数字前面写0或者0x,除非您需要进行八进制转换或者16进制转换

进制转换

a.toString(b)

  • a 为要转换的数字,b为要转换成的进制
  • 注意: b 的区间为[2,36] (可在2到36之间进行转换) ,如果不在这个区间的转换是不支持的(报错)
var num = 38
console.log(num.toString(37)) // 报错
// RangeError: toString() radix argument must be between 2 and 36
1
2
3

parseInt(m,n)

  • 将其他进制转换成10进制
  • m:要转换的数字
  • n:当前要转换的数的进制值

TIP

  • parseInt只能转化为十进制
  • toString可以转化为任意进制,包括十进制
  • 如果是非十进制转化为费十进制,可以先用parseInt转成十进制再用toString转到目标进制

浮点数

  • JavaScript只有一种数值型数据类型,不管是整数还是浮点数,JavaScript都把归为数字
  • JavaScript 中的所有数字都存储为根为 10 的 64 位(8 比特),浮点数
  • JavaScript中的所有数字都是双精度浮点数,是由IEEE754标准制定的64位编码数字

浮点数特点

  • 浮点数是小数点后最少有一位
  • 浮点数科学计数法表示
  • 浮点数最高精度17位小数
  • 浮点数操作存在误差

TIP

  • JavaScript的整数仅仅是双精度浮点数的一个子集,不是单独的一个类型
  • 浮点运算的精度问题,一定要注意

浮点数计算

  • 常规方法:就是把运算数全部存储为整数(无类型),然后格式化显示和计算
  • 浮点数处理和精度问题
  • 推荐工具:mathjs
  • Math.js是一个用于JavaScript和Node.js的扩展数学库。它具有灵活的表达式解析器,支持符号计算,带有大量内置函数和常量,并提供了一个集成的解决方案,可以处理不同的数据类型,如数字,大数,复数,分数,单位和矩阵。功能强大且易于使用

TIP

处理浮点数,如果不考虑太精确的精度问题,我们可以自己处理;如果需要的精度很高,建议用相关别人封装好的处理浮点数的库去做

Number 对象属性

Number.MAX_VALUE

  • JavaScript中最大的数
  • 它的近似值为 1.7976931348623157 x 10的308次方

Number.MIN_VALUE

  • JavaScript中最小的数
  • 接近 0 ,但不是负数
  • 它的近似值为 5 x 10的负324次方

Number.NaN

  • 非数字

Number.NEGATIVE_INFINITY

  • 表示小于 Number.MIN_VALUE 的值
  • 该值代表负无穷大
  • 一个比最小值还小的数字
  • 可以用-Infinity来表示

Number.POSITIVE_INFINITY

  • 表示大于 Number.MAX_VALUE 的值
  • 该值代表正无穷大
  • 一个比最大值还要大的数字
  • 可以用Infinity来表示

Infinity(顶层属性(全局属性))

  • 用于存放表示正无穷大的数值
  • typeof Infinity === number
  • Infinity 不是常量

Number 对象方法

isFinite() 顶层函数(全局函数)

  • isFinite() 属于JavaScript顶层函数(全局函数),不是number专属的

  • 用于检查其参数是否是无穷大

  • 如果 number 是有限数字(或可转换为有限数字),那么返回 true。

  • 如果 number 是 NaN(非数字),或者是正、负无穷大的数,则返回 false

toFixed()

  • 把 Number 四舍五入为指定小数位数的数字

toExponential()

  • 把数字转换成指数计数法
  • 返回值是string类型
  • 参数:
    • 可以省略
    • 规定指数计数法中的小数位数,是 0 ~ 20 之间的值,包括 0 和 20
var number = Number(10000)
console.log(number.toExponential(1)) // 1.0e+4

var number1 = Number(10000)
console.log(number1.toExponential()) // 1e+4

console.log(typeof number1.toExponential()) // string  
1
2
3
4
5
6
7

toPrecision()

  • 在对象的值超出指定位数时将其转换为指数计数法

toString()

  • 把一个 Number 对象转换为一个字符串,并返回结果

toLocaleString()

  • 把一个 Number 对象转换为本地格式的字符串

valueOf()

  • 返回一个 Number 对象的基本数字值
  • 通常由 JavaScript 在后台自动进行调用,而不是显式地处于代码中

NaN

  • Not a Number:表示非数字的值
  • NaN实际上是一个数字
  • NaN与任何值都不相等,与自己也不相等
  • NaN即是number的属性,也是JavaScript的全局属性

TIP

NaN可以归类为Number,typeof NaN 结果为number,这里单独拿出来解析比较方便

typeof NaN

  • typeof NaN 结果是number

isNaN()

  • 既然NaN与自己都不相等,怎么判断是不是NaN呢?
  • isNaN()
console.log(typeof NaN) // number
console.log(NaN == NaN) // false
console.log(NaN === NaN) // false
console.log(isNaN(NaN)) // true
1
2
3
4

TIP

  • NaN表示一个不是数字的东西,尽管它实际上是一个数字。

  • 它不等于它本身,如果要检查是否有东西是NaN时,需要借助**isNaN()**函数。

  • JavaScript中需要使用三重等号 === 来确保两个元素是否相同

Boolean

TIP

在 JS 中,布尔值是一种基本的数据类型。

Boolean 对象是一个将布尔值打包的布尔对象。

Boolean和boolean

  • boolean 是typeof(Boolean)得到的返回值
  • Boolean是一个对象
  • Boolean类型有两个字面值:true和false
  • true不一定等于1,false不一定等于0

创建Boolean

new Boolean(value);	//构造函数
Boolean(value);		//转换函数,普通函数
1
2
  • 构造函数

    • 把参数转换成一个布尔值
    • 返回的是一个包装的对象
  • 普通函数

    • 只将把它的参数转换成一个原始的布尔值,并且返回这个值

为true的情况

  • 任何非空字符串
  • 任何非0数值
  • 任何非空对象

为false的情况

  • 空字符串
  • 0(-0)和NaN
  • undefined和null

symbol

  • symbol是ES6引入了一种新的原始数据类型

  • 表示独一无二的值

  • typeof Symbol() === symbol

symbol特点

  • 从symbol创建开始就是不可变的。你不能为它设置属性(如果你在严谨模式下尝试,会报类型错误)
  • symbol可以作为属性名。这是它的类字符串性质
  • 每一个symbol都是唯一的,绝对不会重复
  • symbol不能被自动转换为字符串,任何试图拼接symbol与字符串将会引起类型错误
    • 当前可以通过把symbol先转为字符串即可(String或者toString方法)

symbol不是对象

  • 生成的 Symbol 是一个原始类型的值,不是对象
  • 使用new Symbol()函数会报错,因为不是对象
  • 不是对象,不能添加属性

symbol标识

  • Symbol函数可以接受一个字符串作为参数,表示对 Symbol 实例的描述
  • 是为了在控制台显示,或者转为字符串时,比较容易区分
// 创建symbol,添加标识
let symbol1 = Symbol('symbolOne');
let symbol2 = Symbol('symbolTwo');

console.log(symbol1) // Symbol(symbolOne)
console.log(symbol2) // Symbol(symbolTwo)

// 转换为字符串(允许)
symbol1.toString() 
symbol2.toString() 

// 转化为number(不允许,会报错)
let symbol3 = Symbol(3)
Number(symbol3) // 报错:Cannot convert a Symbol value to a number

// 转化为布尔值(允许,结果为true)
let symbol4 = Symbol()
console.log(Boolean(symbol4)) // true

console.log(symbol1) // Symbol(symbolOne)
console.log(symbol2) // Symbol(symbolTwo)

// 创建2个symbol对比
let s1 = Symbol()
let s2 = Symbol()

// 每一个symbol都是唯一的,绝对不会重复
console.log(s1 == s2) // false
console.log(s1 === s2) // false

let ret = s1 + 1 // 报错:Cannot convert a Symbol value to a number
let ret1 = s1 + 'abc' // 报错:Cannot convert a Symbol value to a string
let ret1 = s1 + true // 报错:Cannot convert a Symbol value to a number
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33

TIP

  • Symbol 值不能与其他类型的值进行运,会报错
  • Symbol 值可以显式转为字符串和布尔值,但是不能转为数字

字符串

  • JavaScript String 对象用于处理已有的字符块。
  • 字符串String是javascript基本数据类型,同时javascript也支持String对象,它是一个原始值的包装对象。
  • String 对象的 length 属性声明了该字符串中的字符数。
  • String 类定义了大量操作字符串的方法,例如从字符串中提取字符或子串,或者检索字符或子串。

TIP

  • JavaScript 的字符串是不可变的(immutable),String 类定义的方法都不能改变字符串的内容。
  • 像 String.toUpperCase() 这样的方法,返回的是全新的字符串,而不是修改原始字符串。

字符串注意点

  • 字符串创建的时候单引号和双引号不能交替使用
  • 字符串一旦被创建,它的值是无法被改变的,想要改变必须销毁原有的字符串
  • 字符串中添加转义符号,生成转义字符串,它表示一个字符串

单字节和双字节(简单理解)

  • 单字节指只占一个字,是英文字符;双字是占两个字节的,中文字符都占两个字节
  • 计算机中的数据都是以0和1来表示的,其中一个0或者一个1称之为一位,8位称为一个字节(Byte),两个字节称为一个字(Word)(双字节),4个字节称为双字(Dword)(四字节)

字符串创建

  • 使用单引号或双引号创建
var str = "我是字符串"
var str2 = '我也是字符串'
1
2
  • 使用构建函数粗黄建
var str = 'abc'
console.log(typeof str) //类型为string,为基本类型

var str1 = new String(str)
console.log(typeof str1) // 类型为object,为引用类型

var str2 = String(str)
console.log(typeof str2) // 类型为string,为基本类型

// null 和 undefined
let str3 = String(null)
console.log(str3) // null
console.log(typeof str3) // string

let str4 =new String(null)
console.log(str4)  // String {"null"},字符串‘null’对象
console.log(typeof str4) // object


let str5 = String(undefined) 
console.log(str5) // undefined
console.log(typeof str5) // string

let str6 = new String(undefined) 
console.log(str6) // String {"undefined"},字符串‘undefined’对象
console.log(typeof str6) // object
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26

TIP

new String() 和 String()

  • new String() 作为构造函数使用时,它返回一个新创建的 String 对象,返回的是引用存储(new 出来的都是对象嘛)
  • 当不用 new 运算符调用 String() 时,它只把字符串转换成原始的字符串,并返回转换后的值,通熟一点将就是:String()直接使用返回的是值存储

length

  • 字符串的长度
  • length 属性统计的是字符个数,而不是字符串长度(字节数)
  • 所以length统计双字节字符长度是不准确的(必须汉字)
// 英文、数字、符号均为一个字节,汉字为两个
console.log('1sS#峰'.length) // 结果是5,实际结果应该是6
1
2

转义符

我们在做JavaScript字符串处理的过程中,会遇到很多特殊字符处理的问题,稍微不注意,这些特殊字符都会给我们带来许多问题

容易出问题的地方

  • html页面显示,转义符导致一些效果无法达到
  • 特殊字符串转义符会导致JSON.stringfy()被截断(无法把一个完整的对象转成字符串)
  • 特殊字符,在网络传输或者读取的过程中,可能会出现丢失报错
转义序列 字符
\b 退格
\f 走纸换页
\n 换行
\r 回车
\t 横向跳格 (Ctrl-I)
\' 单引号
\" 双引号
\\ 反斜杠
\xNN 由两位十六进制数值NN指定的Latin-1字符
\uNNNNN 由四位十六进制数NNNN指定的Unicode字符
\NNN 由一位到三位八进制数(1到377)指定的Latin-1字符。
ECMAScript v3不支持,不要使用这种转义序列

eval()

  • 计算 JavaScript 字符串,并把它作为脚本代码来执行
  • eval可以解析字符串,和JSON.parse()作用类似,但是也是有区别的
  • eval方法只能在非严格模式中进行使用,在use strict中是不允许使用这个方法的
  • eval存在安全性和性能方面的问题
  • 老生常谈:在任何情况下我们都应该避免使用 eval 函数。99.9% 使用 eval 的场景都有不使用 eval 的解决方案

TIP

其实针对eval,不同的人有不同的看法,这里列举出来其实是为了应付一些稀奇古怪小的面试

经常有人把eval和JSON.parse()结合起来当做面试题来为难我们(哭晕在厕所,别问我为啥在厕所...)

小峰哥不是大牛,只能随大流了,大家可以看下这一篇文章,或许会改变你的看法

eval()不是魔鬼,只是被误解了(翻译)

字符串查找方法

charAt()

  • 返回指定位置的字符
  • 字符串中第一个字符的下标是 0。
  • 如果参数 index 不在 0 与 string.length 之间,该方法将返回一个空字符串
var str="Hello world!"
console.log(str.charAt(1)) // 结果e
1
2

charCodeAt()

  • 返回在指定的位置的字符的 Unicode 编码。
  • 这个返回值是 0 - 65535 之间的整数
  • 字符串中第一个字符的下标是 0。
  • 如果 index 是负数,或大于等于字符串的长度,则 charCodeAt() 返回 NaN
var str="Hello world!"
console.log(str.charCodeAt(1)) // 101
console.log(str.charAt(1)) // 下标 1
1
2
3

TIP

  • charCodeAt() 返回的是位于指定位置的字符的编码, charAt() 方法返回的是字符子串
  • 特殊情况charAt()返回的是空字符串,charCodeAt() 返回的是Nan

fromCharCode()

  • 接受一个指定的 Unicode 值,然后返回一个字符串
  • 方法是 String 的静态方法,字符串中的每个字符都由单独的 Unicode 数字编码指定
var n = String.fromCharCode(72,69,76,76,79);

console.log(n) //HELLO
1
2
3

字符串位置方法

indexOf()

  • 返回某个指定的字符串值在字符串中首次出现的位置,即使是多次出现,也返回第一次出现的位置
  • 找不到,返回-1
  • str.indexOf(searchvalue,fromindex)
  • 参数:
    • 参数searchvalue:需要检索的字符串
    • 参数fromindex:
      • 可选
      • 规定在字符串中开始检索的位置。
      • 它的合法取值是 0 到 stri.length - 1。
      • 如省略该参数,则将从字符串的首字符开始检索。
var arr = 'hello world ! world'
console.log(arr.indexOf('world')) // 6 只返回第一次出现的位置
console.log(arr.indexOf('World')) // -1
1
2
3

TIP

  • indexOf() 方法对大小写敏感,区分大小写
  • 如果要检索的字符串值没有出现,则该方法返回 -1,number类型的 -1

lastIndexOf()

  • 返回一个指定的字符串值最后出现的位置
  • 在指定位置从后向前搜索
  • str.lastIndexOf(searchvalue,fromindex)
  • 参数:
    • searchvalue
      • 需要检索的字符串
    • fromindex
      • 可选
      • 规定在字符串中开始检索的位置。
      • 它的合法取值是 0 到 stringObject.length - 1。
      • 如省略该参数,则将从字符串的最后一个字符处开始检索。
var arr = 'abcb'
console.log(arr.length) // 4
console.log(arr.lastIndexOf('b')) // 3 返回的下标是从左向右的
console.log(arr.lastIndexOf('b',0)) // -1
console.log(arr.lastIndexOf('b',1)) // 1
console.log(arr.lastIndexOf('b',2)) // 1
console.log(arr.lastIndexOf('b',3)) // 3
console.log(arr.lastIndexOf('b',4)) // 3

var arr = 'abcbc'
console.log(arr.length) // 5
console.log(arr.lastIndexOf('bc')) // 3 返回的下标是从左向右的
console.log(arr.lastIndexOf('bc',0)) // -1
console.log(arr.lastIndexOf('bc',1)) // 1
console.log(arr.lastIndexOf('bc',2)) // 1
console.log(arr.lastIndexOf('bc',3)) // 3
console.log(arr.lastIndexOf('bc',4)) // 3
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17

TIP

  • fromindex取值是从左向右,正常0到length开始的
  • 如果是获取一个字符串(而不是单个字符),fromindex位置如果是获取字符串所占用的索引,就能获得数据
  • 比如上面在abcbc中搜索bc,输入1,1其实是abcbc中第一个bc的b占的位置,这时,就可以获取到从右向左第一个bc的位置了

字符串匹配方法

match()

  • 可在字符串内检索指定的值,或找到一个或多个正则表达式的匹配
  • 类似 indexOf() 和 lastIndexOf(),但是它返回指定的值,而不是字符串的位置
  • 存放匹配结果的数组,数组的内容依赖于regexp是否具有全局变量g
var str="123abc456def789hji8"

// 全局匹配
var arr =str.match(/\d+/g)
console.log(arr); // ["123", "456", "789", "8"]

// 非全局匹配
var arr2 =str.match(/\d+/)
console.log(arr2); // ["123", index: 0, input: "123abc456def789hji8", groups: undefined]
1
2
3
4
5
6
7
8
9
  • 全局匹配g
    • match() 方法将执行全局检索,找到 str中的所有匹配子字符串
    • 若没有找到任何匹配的子串,则返回 null。
    • 如果找到了一个或多个匹配子串,则返回一个数组。
  • 非全局匹配(没有g)
    • match() 方法就只能在 str中执行一次匹配
    • 如果没有找到任何匹配的文本, match() 将返回 null。
    • 匹配到了将返回一个数组,其中存放了与它找到的匹配文本有关的信息。
      • 该数组的第 0 个元素存放的是匹配文本,而其余的元素存放的是与正则表达式的子表达式匹配的文本。
      • 返回的数组还含有两个对象属性。
        • index 属性声明的是匹配文本的起始字符在 str 中的位置
        • input 属性声明的是对 str 的引用。

TIP

  • jQuery的init方法中就巧妙的使用了match方法
  • match()方法和exec()方法还是有不同的使用场景的,后面会讲
  • 检索字符串中指定的子字符串,或检索与正则表达式相匹配的子字符串
  • 如果没有找到任何匹配的子串,则返回 -1。
  • 返回str中第一个与 regexp 相匹配的子串的起始位置
  • search() 对大小写敏感
var str = "1aabbaa"

// 默认从左向右,只匹配第一次出现的
var index = str.search('aa')
console.log(index) // 1

// 配置i 忽略大小写
var index1 = str.search(/aA/i)
console.log(index1) // 1

// search对大小写敏感
var index2 = str.search(/aA/)
console.log(index2) // -1
1
2
3
4
5
6
7
8
9
10
11
12
13

TIP

  • search() 方法不执行全局匹配,它将忽略标志 g。

  • 它同时忽略 regexp 的 lastIndex 属性,并且总是从字符串的开始进行检索,它总是返回 str的第一个匹配的位置。

  • 要执行忽略大小写的检索,请追加标志 i

replace()

  • 在字符串中用一些字符替换另一些字符,或替换一个与正则表达式匹配的子串
  • 参数
    • 参数1:可以是字符串或者是正则表达式
    • 参数2:规定了替换文本或生成替换文本的函数
  • 返回一个新的字符串
  • 默认只替换第一个匹配子串,想要全局匹配,需要g
var s = "www xuefeng666 com ww";

// 全局匹配
var re = /ww/g;
var result = s.replace(re, "a");
console.log(result); // aw xuefeng666 com a

// 匹配不到
var result2 = s.replace('demo', "a");
console.log(result2); // www xuefeng666 com ww

// 非全局匹配,只替换第一个匹配到的
var result3 = s.replace(/ww/, "a");
console.log(result3); // aw xuefeng666 com ww
1
2
3
4
5
6
7
8
9
10
11
12
13
14

split()

  • 把一个字符串分割成字符串数组
  • str.split(separator,howmany)
  • 参数:
    • separator
    • howmany

字符串拼接方法

concat()

  • 用于连接两个或多个字符串,注意数组也有concat()方法
  • 该方法不改变原字符串
  • concat() 方法将把它的所有参数转换成字符串,然后按顺序连接到字符串调用者的尾部,并返回连接后的字符串。
var arr = 'zs';
var arr1 = ['ww','zl'];
var arr2 = arr.concat(arr1);
console.log(arr) // zs
console.log(arr2) // zsww,zl
console.log(arr + arr1) // zsww,zl
1
2
3
4
5
6

TIP

在现代浏览器,尽量用 + ,更高效,大部分现代浏览器性能 + 高于concat

字符串截取方法

slice()

  • 可提取字符串的某个部分,并以新的字符串返回被提取的部分
  • str.slice(start,end)
  • 参数:
    • start
      • 要抽取的片断的起始下标。
      • 如果是负数,则该参数规定的是从字符串的尾部开始算起的位置。
      • 也就是说,-1 指字符串的最后一个字符,-2 指倒数第二个字符,以此类推。
    • end
      • 紧接着要抽取的片段的结尾的下标。
      • 若未指定此参数,则要提取的子串包括 start 到原字符串结尾的字符串。
      • 如果该参数是负数,那么它规定的是从字符串的尾部开始算起的位置。
var str = "abcd"
console.log(str.slice()); // abcd 不加参数,就是原样返回
console.log(str.slice(0)); // abcd 参数end不指定,就是start到字符串结尾部分

console.log(str.slice(1,2)); // b 不包含结尾
console.log(str.slice(1,3)); // bc 不包含结尾

console.log(str.slice(3,-1)); // 空字符串 start大于等于end,返回空字符串

console.log(str.slice(-2,-1)); // c -2就是倒数第二个字符,-1倒数第一个字符(也就是,(2,3))
console.log(str.slice(2,3)); // c

console.log(str.slice(-1)); // d 未指定end,返回的是start到结尾的字符串
1
2
3
4
5
6
7
8
9
10
11
12
13

TIP

  • 若指定end参数,截取数据不包括end位置

  • 若第一个参数等于大于第二个参数,则返回空字符串.

  • 如果不指定参数,就返回一个一模一样的原字符串,相当于克隆一份

  • String.slice() 与 Array.slice() 相似,正好用来拷贝数据

substr()

  • 可在字符串中抽取从 start 下标开始的指定数目的字符。
  • str.substr(start,length)
  • 参数:
    • start:
      • 必需
      • 要抽取的子串的起始下标,必须是数值。
      • 如果是负数,那么该参数声明从字符串的尾部开始算起的位置。
      • 也就是说,-1 指字符串中最后一个字符,-2 指倒数第二个字符,以此类推。
    • length:
      • 可选。
      • 子串中的字符数。必须是数值。
      • 如果省略了该参数,那么返回从 str 的开始位置到结尾的字串。
      • 如果 length 为 0 或负数,将返回一个空字符串
  • 返回一个新的字符串
var str = "hello World";

var str1 = str.substr(2, 5); 
console.log(str1); // llo W

var str2 = str.substr(2,0)
console.log(str2) // 空字符串

// 第一个参数是负数
var str3 = str.substr(-3,2)
console.log(str3) // rl
1
2
3
4
5
6
7
8
9
10
11

substring()

  • 提取字符串中介于两个指定下标之间的字符。
  • str.substring(start,stop)
  • 参数:
    • start:
      • 必需。
      • 非负整数,要提取的子串的第一个字符在 str 中的开始索引
    • stop:
      • 可选。
      • 非负整数,指明子字符串的结束位置,该索引从 0 开始起算
      • 如果省略,则返回到字符串结尾的子串
  • 返回的字符串是start到stop,长度为stop减去start,也就是substring 方法将返回一个包含从 start 到最后(不包含 end )的子字符串的字符串
  • 如果参数 startstop 相等,那么该方法返回的就是一个空串(即长度为 0 的字符串)
var str = "abcdefghijk"

// 如果 start 或 end 为 NaN 或者负数,那么将其替换为0
console.log(str.substring(-2,2)) // ab
console.log(str.substring(-0,2)) // ab

// substring 方法使用 start 和 end 两者中的较小值作为子字符串的起始点
console.log(str.substring(3,1)) // bc
console.log(str.substring(1,3)) // bc

// start和stop相等,返回空字符串
console.log(str.substring(1,1)) // 空字符串
1
2
3
4
5
6
7
8
9
10
11
12

TIP

  • substring 方法使用 start 和 end 两者中的较小值作为子字符串的起始点。
  • 例如, str.substring(0, 3) 和 str.substring(3, 0) 将返回相同的子字符串。
  • 如果 start 或 end 为 NaN 或者负数,那么将其替换为0。

substr() vs substring()

  • 与 slice() 和 substr() 方法不同的是,substring() 不接受负的参数
  • substr()含头含尾,substring()含头不含尾,slice()也是含头不含尾

空格处理

trim()

  • 去除字符串两边的空白

trimLeft()

  • 清除字符左边空格

trimRight()

  • 清除字符右边空格

字符串常规编码与解码

escape()

  • 对字符串进行编码,这样可以在所有计算机上读取该字符串;

unescapae()

  • 对unescape()函数编码的字符串解码

URI字符串编码与解码

encodeURI()

  • 把字符串作为URI进行编码

decodeURI()

  • 对encodeURI()函数编码的字符串解码

URI组件编码与解码

encodeURIComponent()

  • 把字符串作为URI组件进行编码

decodeURIComponent()

  • 对encodeURIComponent()函数编码的字符串解码

转为大写

toUpperCase()

  • 把小写字符串转为大写
  • 返回所有小写字符都被转换为大写的字符串

toLocaleUpperCase()

  • 少数语言(如土耳其语言)会为Unicode大小写转换应用特殊的规则,这时候就必须使用针对地区的方法来保证实现正确的转换
  • 一般来说,在不知道自己的代码将在那种语言环境中运行的情况下,还是使用针对地区的方法更稳妥一些

转为小写

toLowerCase()

  • 把大写字符串转为小写
  • 返回所有大写字符都被转换为小写的字符串

toLocaleLowerCase()

  • 同toLocaleUpperCase解释

toString()

  • 返回字符串
  • 一般不会调用该方法,直接加上一个空字符串即可
  • 当调用该方法的对象不是 String 时抛出 TypeError 异常

比较方法

localeCompare()

  • 用本地特定的顺序来比较两个字符串。
  • 针对于localeCompare()(比较两个字符串,考虑了默认的本地排序规则),使用的本地规则有汉字和英语的,例子中用的是英语,则是根据字母排序的。如果有用到汉字则是根据汉字拼音来排序。

数组

数组定义

  • 就是将多个元素(通常是同一类型)按一定顺序排列放到一个集合中,那么这个集合我们就称之为数组。
    • 数组就是一组数据的集合
    • 其表现形式就是内存中的一段连续的内存地址、
    • 数组名称其实就是连续内存地址的首地址

数组特点

  • 数组是一个有序的列表
  • 数组定义时无需指定数据类型,可以在数组中存放任意的数据
  • 数组定义时可以无需指定数组长度

数组创建

直面量创建数组

var arr = [];//创建一个空数组

var arr2 = [1,2,3];//创建一个有三个元素的数组
1
2
3

构造函数创建数组

var arr1 = new Array();//创建空数组

var arr2 = new Array(10)//创建一个长度为10的数组

var arr3 =  new Array(5,4,3,2,1)//创建数组并初始化,添加数组
1
2
3
4
5

TIP

  • 如果调用构造函数 Array() 时没有使用参数,那么返回的数组为空,length 字段为 0。
  • 当调用构造函数时只传递给它一个数字参数,该构造函数将返回具有指定个数、元素为 undefined 的数组。

数组操作

length

  • 可设置或返回数组中元素的数目
  • 数组的 length 属性总是比数组中定义的最后一个元素的下标大 1
  • 对于那些具有连续元素,而且以元素 0 开始的常规数组而言,属性 length 声明了数组中的元素的个数。
  • 设置 length 属性可改变数组的大小。
    • 如果设置的值比其当前值小,数组将被截断,其尾部的元素将丢失。
    • 如果设置的值比它的当前值大,数组将增大,新的元素被添加到数组的尾部,它们的值为 undefined。

设置或访问数组元素

// 格式:数组名[下标]
// 下标就是索引
// 功能:获取数组对应下标的那个值,如果下标不存在,则返回undefined。
var arr = ['tom', 'marry', 'bob'];
arr[0];	// tom
arr[2]; // bob
arr[3]; // undefined,因为数组的最大下标为2,访问3数组越界了,别的编程语言有些会报错


arr[0] = 'xiaofengge';//重新设置第一个元素
arr[0];//xiaofengge  这个时候数据就变了
1
2
3
4
5
6
7
8
9
10
11

遍历数组

  • 在JavaScript中,数组的数据类型其实就是对象
  • JavaScript中的For.....in语句可以实现对一个对象的所有属性的遍历
  • 所以我们可以使用for...in语句实现对一个数组的所有元素的遍历
  • 数组中有几个元素,for..in语句就循环执行多少次
var x  
var arr = new Array()  
arr[0] = "hello"  
arr[1] = "world"  
arr[2] = "!"  
  
for (x in arr)  {  
    console.log(arr[x])
} 

// 结果
// hello   
// world 
// !
1
2
3
4
5
6
7
8
9
10
11
12
13
14
  • 可以使用for循环遍历数组
var arr = [11,43,665,21,342,54,66]
for(var i = 0; i < arr.length; i++) {
	// 数组遍历的固定结构
}
1
2
3
4

TIP

不同类型的循环

JavaScript 支持不同类型的循环:

  • for - 循环代码块一定的次数
  • for/in - 循环遍历对象的属性
  • while - 当指定的条件为 true 时循环指定的代码块
  • do/while - 同样当指定的条件为 true 时循环指定的代码块

常见数组方法

push()

  • 向数组的末尾添加一个或多个元素,并返回新的长度。
    • 末尾添加
    • 返回的是长度
    • 会改变原数组
    • 可以一次添加多个元素push
var arr1 = [11,22,33,44];
var arr2 = arr1.push(55);
console.log(arr1);  //[11,22,33,44] 
console.log(arr2);  // push 返回的是数组长度,5
1
2
3
4

pop()

  • 用于删除并返回数组的最后一个元素
    • 返回最后一个元素
    • 会改变原数组。
var arr = [11,22,33];
console.log(arr.pop()); //33
console.log(arr);  //[11,22]
1
2
3

shift()

  • 把数组的第一个元素从其中删除,并返回第一个元素的值
    • 返回第一个元素
    • 改变原数组
var arr = [11,22,33];
console.log(arr.shift()); //11
console.log(arr);  //[22,33]
1
2
3

unshift()

  • 向数组的开头添加一个或更多元素,并返回新的长度
    • 返回新长度
    • 改变原数组。
var arr = [11,22,33,44];
console.log(arr.unshift(3,6)); //6  添加元素并返回新的长度
console.log(arr); //[3, 6, 11, 22, 33, 44]
1
2
3

TIP

unshift()可以不传参数,不传参数就是不增加元素

concat()

  • 用于连接两个或多个数组
  • 该方法不会改变现有的数组,而仅仅会返回被连接数组的一个副本
  • 该参数可以是具体的值,也可以是数组对象,可以是任意多个
var arr = ['zs','ls'];
var arr1 = ['ww','zl'];

// 参数是具体的值,并且结果不改变原素组
console.log(arr.concat('xiaofengge')) // ["zs", "ls", "xiaofengge"]
console.log(arr) //  ["zs", "ls"]

var arr2 = arr.concat(arr1); // 参数是一个数组 
console.log(arr2) // ["zs", "ls", "ww", "zl"] ,并且说明concat()不改变原数组
1
2
3
4
5
6
7
8
9

TIP

  • Concat()返回一个新的数组。
  • 通过把所有参数添加到调用者中生成的。
  • 如果要进行 concat() 操作的参数是数组,那么添加的是数组中的元素,而不是数组。

join()

  • 用于把数组中的所有元素放入一个字符串。
    • 元素是通过指定的分隔符进行分隔的
    • 默认使用','号分割
    • 不改变原数组
    • 常和字符串方法split一块使用,效果相反
var arr = [11,22,33];
console.log(arr.join());  //11,22,33  默认使用逗号
console.log(arr);  //[11, 22, 33]不改变原来的数组
1
2
3

reverse()

  • 用于颠倒数组中元素的顺序。
    • 返回的是颠倒后的数组
    • 会改变原数组
var arr = [11,22,33];
console.log(arr.reverse()); //[33, 22, 11] 翻转数组
console.log(arr);  //[33, 22, 11] 会改变原数组
1
2
3

sort 排序

  • 按照 Unicode code 位置排序,默认升序

  • 如果调用该方法时没有使用参数,将按字母顺序对数组中的元素进行排序,是按照字符编码的顺序进行排序

  • 要实现这一点,首先应把数组的元素都转换成字符串(如有必要),以便进行比较。

  • array.sort()方法默认是升序排序,

    • 如果想按照其他标准进行排序,就需要提供比较函数,该函数要比较两个值
    • 然后返回一个用于说明这两个值的相对顺序的数字。
    • 比较函数应该具有两个参数 a 和 b,其返回值如下:
      • 若 a 小于 b,在排序后的数组中 a 应该出现在 b 之前,则返回一个小于 0 的值。
      • 若 a 等于 b,则返回 0。
      • 若 a 大于 b,则返回一个大于 0 的值。

本质:比较函数两个参数a和b,返回a-b升序,返回b-a降序

var arr = [4,3,6,5,7,2,1];
arr.sort();
console.log(arr);
//输出结果[1,2,3,4,5,6,7]

var arr = [4,3,6,5,7,2,1];
arr.sort();
arr.sort(function(a,b){
    return b-a;
});
console.log(arr);
//输出结果[7,6,5,4,3,2,1]
1
2
3
4
5
6
7
8
9
10
11
12
  • 按照数组对象中某个属性值进行排序
var arr= [ 
    { 'sortNum': 2},
    { 'sortNum': 1},
    { 'sortNum': 5},
    { 'sortNum': 6},
    { 'sortNum': 7},
    { 'sortNum': 3},
    { 'sortNum': 9},
    { 'sortNum': 4},
    { 'sortNum': 0}
];

arr.sort(function(a, b){
    return a.sortNum - b.sortNum;// 升序排序
});
console.log(arr);
// 0: {sortNum: 0}
// 1: {sortNum: 1}
// 2: {sortNum: 2}
// 3: {sortNum: 3}
// 4: {sortNum: 4}
// 5: {sortNum: 5}
// 6: {sortNum: 6}
// 7: {sortNum: 7}
// 8: {sortNum: 9}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25

slice()

  • 返回一个新的数组,包含从 start 到 end (不包括该元素)的 arrayObject 中的元素。
    • 返回选定的元素
    • 该方法不会修改原数组
var arr = [11,22,33,44,55];
console.log(arr.slice(1,3));  //[22,33]  返回选择的元素
console.log(arr);  //[11,22,33,44,55] 不会改变原数组
1
2
3

splice()

  • splice() 方法可删除从 index 处开始的零个或多个元素,并且用参数列表中声明的一个或多个值来替换那些被删除的元素。
  • 如果从数组中删除了元素,则返回的是含有被删除的元素的数组。
  • splice() 方法会直接对数组进行修改
    • start:整数,规定添加/删除项目的位置,使用负数可从数组结尾处规定位置。
    • deleteCount:要删除的项目数量。如果设置为 0,则不会删除项目。
    • options:向数组添加的新项目(可选)
  • 返回值:
    • 包含被删除项目的新数组,如果有的话。
var a = [11,22,33,44];
console.log(a.splice(1,0,88)); //[]  么有删除元素,所以返回空数组
console.log(a);  // [11, 88, 22, 33, 44]  第三个参数,添加了88元素,在索引1位置


var b = [11,22,33,44];
console.log(b.splice(1,2,3));  //[22, 33]  返回删除的元素组成的数组
console.log(b); //[11, 3, 44] 尕布了原有的数组
1
2
3
4
5
6
7
8

indexOf()

  • 返回数组中某个指定的元素位置
  • 该方法将从头到尾地检索数组,看它是否含有对应的元素。
  • 开始检索的位置在数组 start 处或数组的开头(没有指定 start 参数时)。
    • 如果找到一个 item,则返回 item 的第一次出现的位置。
    • 开始位置的索引为 0。
  • 如果在数组中没找到指定元素则返回 -1。
var arr = [11, 22, 33, 33];
console.log(arr.indexOf(33)); // 2 第一次出现的位置
console.log(arr.indexOf(44)); // -1  不存在,-1

if (arr.indexOf(44) === -1) {
    // 元素不存在数组中
    console.log('44不在数组arr中')
}
1
2
3
4
5
6
7
8

lastIndexOf()

  • 返回一个指定的元素在数组中最后出现的位置,在一个数组中的指定位置从后向前搜索。
  • 如果要检索的元素没有出现,则该方法返回 -1。
  • 该方法将从尾到头地检索数组中指定元素 item。
    • 开始检索的位置在数组的 start 处或数组的结尾(没有指定 start 参数时)。
    • 如果找到一个 item,则返回 item 从尾向前检索第一个次出现在数组的位置
    • 数组的索引开始位置是从 0 开始的。
  • 如果在数组中没找到指定元素则返回 -1。
var arr = [11, 22, 33, 22];
console.log(arr.lastIndexOf(22));     // 3
console.log(arr.lastIndexOf(7));     // -1  找不到返回-1
console.log(arr.lastIndexOf(22, 3));  // 3  索引3开始
console.log(arr.lastIndexOf(22, 2));  // 1  索引1开始
console.log(arr.lastIndexOf(22, -2)); // 1  
console.log(arr.lastIndexOf(22, -1)); // 3
1
2
3
4
5
6
7

TIP

start可以是负数,索引0是数组第一个元素,那么-1是数组倒数第一个元素,以此类推

toString()

  • 把数组转换为字符串,并返回结果。
  • 返回值与没有参数的 join() 方法返回的字符串相同。
var arr = new Array(3)
arr[0] = "11"
arr[1] = "22"
arr[2] = "33"
console.log(arr.toString()) // 11,22,33
1
2
3
4
5

forEach()

  • 用于调用数组的每个元素,并将元素传递给回调函数。
    • 没有返回值
    • forEach() 对于空数组是不会执行回调函数的
    • callback的参数:
      • value --当前索引的值 必选
      • index --索引 可选
      • array --原数组 可选
var arr = [11223344]
arr.forEach((item, index) => {
    // item 当前元素,必选
    // index 当前元素的索引
    // 匿名函数,是里面每一个item都会执行一次
})
1
2
3
4
5
6

map()

  • 返回一个新数组,数组中的元素为原始数组元素调用函数处理后的值。
  • 按照原始数组元素顺序依次处理元素
  • map() 不会对空数组进行检测
  • map() 不会改变原始数组
let arr = [11,22,33,44,55]
let ret = arr.map( (value,index,array)=>{
    console.log('value',value)
    console.log('index',index)
    console.log('array',array)
})   
console.log(arr)
console.log(ret)
1
2
3
4
5
6
7
8

TIP

arr.forEach()和arr.map()

  • arr.forEach()是和for循环一样,是for循环的替代品
  • arr.map()是修改数组其中的数据,并返回新的数据
    • arr.forEach() 没有return
    • arr.map() 有return

filter()

  • 创建一个新的数组,新数组中的元素是通过检查指定数组中符合条件的所有元素
  • 返回true的项组成的数组
    • filter() 不会对空数组进行检测。
    • filter() 不会改变原始数组。
let arr = [11,22,33,44,55]
let arr1 = arr.filter( (i, v) => {
    if(i < 33) {
        return true
    }
})
console.log(arr1)    // [11, 22]
1
2
3
4
5
6
7

TIP

filter常用来过滤符合条件的数据,过滤数组

some()

  • 数组中的每一项给定特定的函数,如果任一项返回true,则返回true
  • 数组中任意一个元素满足条件,返回值就是true
function compare(element, index, array) {
  return element > 44;
}    
[11, 22, 33, 44, 55].some(compare);  //true   有一个满足条件的,返回true
[11, 22, 33, 44].some(compare); // false  没有满足条件的,返回false
1
2
3
4
5

TIP

some常用来检测数组是否包含符合条件的元素

every()

  • 对数组的每一项都运行给定的函数,每一项都返回 ture,则返回 true
  • 如果有一项返回false,那么就返回false,并且结束循环
var arr = [11, 22, 33, 44];
var boolValue = arr.every(function (t) {
    return t + 1 > 10;
});
console.log(boolValue); //true 每一项加1,都大于10
1
2
3
4
5

TIP

every常用来检测数组中所有的元素是否合法

reduce()

  • 返回数累加的值 累加器
  • 迭代数组的所有项,累加器,数组中的每个值(从左到右)合并,最终计算为一个值
  • arr.reduce(callback, initialValue)
  • callback:
    • previousValue 必选 --上一次调用回调返回的值,或者是提供的初始值(initialValue)
    • currentValue 必选 --数组中当前被处理的数组项
    • index 可选 --当前数组项在数组中的索引值
    • array 可选 --原数组
  • initialValue: 可选 --初始值
let arr = [11,22,33,44]
let arr1 = arr.reduce((preValue, curValue) => 
    preValue + curValue
)
console.log(arr1)    // 110
1
2
3
4
5

TIP

reduce常用来累加数据

reduceRight()

  • 和arr.reduce()功能一样
  • reduceRight()从数组的末尾向前将数组中的数组项做累加
let arr = [11,22,33,44]
let arr1 = arr.reduceRight((preValue, curValue) => 
    preValue + curValue
)
console.log(arr1)    // 110
1
2
3
4
5

TIP

reduce的定制版

ES6新增数组方法

from()

  • 将两类对象转为真正的数组
    • 类似数组的对象(array-like object)
    • 可遍历(iterable)的对象(包括 ES6 新增的数据结构 Set 和 Map)
let arrayLike = {
    '0': 'a',
    '1': 'b',
    '2': 'c',
    length: 3
};

// ES5的写法
var arr1 = [].slice.call(arrayLike); // ['a', 'b', 'c']

// ES6的写法
let arr2 = Array.from(arrayLike); // ['a', 'b', 'c']


// 转化NodeList对象
let ps = document.querySelectorAll('p');
Array.from(ps).filter(p => {
  return p.textContent.length > 100;
});

// 转化arguments对象
function foo() {
  var args = Array.from(arguments);
  // ...
}

// 转化字符串,因为字符串有length属性,也可以通过下标访问数据
Array.from('hello')
// ['h', 'e', 'l', 'l', 'o']

// 转化set
let namesSet = new Set(['a', 'b'])
Array.from(namesSet) // ['a', 'b']
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33

TIP

  • 如果参数是一个真正的数组,Array.from会返回一个一模一样的新数组
  • 对于还没有部署该方法的浏览器,可以用Array.prototype.slice方法替代
  • Array.from还可以接受第二个参数,作用类似于数组的map方法,用来对每个元素进行处理,将处理后的值放入返回的数组。

of()

  • 将一组值,转换为数组
Array.of() // []  没有参数,返回空数组
Array.of(undefined) // [undefined]
Array.of(3, 11, 8) // [3,11,8]
Array.of(3) // [3]
Array.of(3).length // 1
1
2
3
4
5

TIP

可以用来替代Array()new Array(),解决由于参数不同而导致一些问题

find()

  • 传入一个回调函数,找到数组中符合当前搜索规则的第一个元素,返回它,并且终止搜索。
let arr = [11,22,33,44,55,22,33]
let arr1 = arr.find((value, index, array) =>value > 22)
console.log(arr1)   // 33  找到33,就直接返回,停止
1
2
3

findIndex()

  • 传入一个回调函数,找到数组中符合当前搜索规则的第一个元素,返回它的下标,终止搜索。
let arr = [11,22,33,44,55]
let arr1 = arr.findIndex((value, index, array) => value > 22)
console.log(arr1)  // 2
1
2
3

fill()

  • 使用给定的值,填充一个数组
  • 填充完后会改变原数组
  • arr.fill(target, start, end)
    • target -- 待填充的元素
    • start -- 开始填充的位置-索引
    • end -- 终止填充的位置-索引(不包括该位置)
let arr = [11, 22, 33, 44, 55]
let arr1 = arr.fill(88)
console.log(arr1)  // [88, 88, 88, 88, 88]
console.log(arr)   // [88, 88, 88, 88, 88]

let arr2 = arr.fill(5, 2)
console.log(arr2) // [88, 88, 5, 5, 5]

let arr3 = arr.fill(5, 1, 3)
console.log(arr3) // [88, 5, 5, 5, 5]
1
2
3
4
5
6
7
8
9
10

copyWithin()

  • 在当前数组内部,将制定位置的数组复制到其他位置
  • 会覆盖原数组项
  • 返回当前数组
  • arr.copyWithin()
    • target --必选 索引从该位置开始替换数组项
    • start --可选 索引从该位置开始读取数组项,默认为0.如果为负值,则从右往左读。
    • end --可选 索引到该位置停止读取的数组项,默认是Array.length,如果是负值,表示倒数
let arr = [11, 22, 33, 44, 55, 66, 77]
let arr1 = arr.copyWithin(1)
console.log(arr1)   // [11, 11, 22, 33, 44, 55, 66]
let arr2 = arr.copyWithin(1, 2)
console.log(arr2)   //  [11, 22, 33, 44, 55, 66, 66]
let arr3 = arr.copyWithin(1, 2, 4)
console.log(arr3)   // [11, 33, 44, 44, 55, 66, 66]
1
2
3
4
5
6
7

includes

  • 判断数中是否包含给定的值
let arr = [11, 22, 33, 44, 55]
let arr1 = arr.includes(33)
console.log(arr1)   // ture

let arr2 = arr.includes(88)
console.log(arr2)    // false

let arr3 = [11, 22, 33, NaN].includes(NaN)
console.log(arr3)  // true
1
2
3
4
5
6
7
8
9

TIP

includes与indexOf()

  • indexOf()返回的是数值,而includes()返回的是布尔值

  • indexOf() 不能判断NaN,返回为-1 ,includes()则可以判断

entries()

  • 遍历数组的键名和键值
let arr = [11, 22, 33, 44]
let arr1 = arr.entries()
for (let e of arr1) {
    console.log(e);  
    // [0, 11]
    // [1, 22]
    // [2, 33]
    // [3, 44]
}
1
2
3
4
5
6
7
8
9

keys()

  • 遍历数组的键名
let arr = [11, 22, 33, 44]
let arr2 = arr.keys()
for (let key of arr2) {
    console.log(key);   // 0,1,2,3
}
1
2
3
4
5

values()

  • 遍历数组键值
let arr = [11,22, 33, 44]
let arr1 = arr.values()
for (let val of arr1) {
    console.log(val);   // 11,22,33,44
}
1
2
3
4
5

函数

构造函数

  • 构造函数可以创建对象

特点:

  • 函数名首字母大写(一般规范)
  • 通过this来给对象添加属性和方法

构造函数执行过程

  • 当使用构造函数创建对象,也就是说 new 构造函数()时,内部就执行啦new object()
  • 将构造函数的作用域给新对象,(既new Object()创造出来的对象)而函数体内的this就代表new object()出来的对象
  • 执行构造函数内部的代码,返回新对象不需要return也能返回
function Person(name,age){
    this.name = name
    this.age = age
    this.sayHello = function(){
        console.log('你好!')
    }
}

// new关键字:使用自定义的构造函数去创建对那么new不能省略
var p1 = new Person('zs',18)
var p2 = new Person('ls',28)

console.log(p1) // Person { name: 'zs', age: 18, sayHello: [Function] }
console.log(p2) // Person { name: 'ls', age: 28, sayHello: [Function] }
console.log(typeof p1) // object

console.log(p1.__proto__.constructor) // [Function: Person]  构造函数
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17

TIP

构造函数内部会创建一个实例,调用普通函数时则不会创建新的对象

构造函数内部的this指向是新创建的person实例,而普通函数内部的this指向调用函数的对象(如果没有对象调用,默认为window)

对象

原型和原型链