重学ES - 1(let、const、var、作用域 & 解构赋值 & 数组遍历)

重学ES - 1

一、let、const与var

知识点前导:

1、作用域
2、delete:删除对象上的属性与变量,但不可以直接删除变量

作用域:

对象类型
global/window全局作用域
function函数作用域(局部作用域)
{}块状作用域(es6新增)
this动态作用域

TIP
如果一个 变量 或者其他表达式不在 “当前的作用域”,那么JavaScript机制会继续沿着作用域链上查找直到全局作用域(global或浏览器中的window)如果找不到将不可被使用。 作用域也可以根据代码层次分层,以便子作用域可以访问父作用域,通常是指沿着链式的作用域链查找,而不能从父作用域引用子作用域中的变量和引用

全局作用域:

变量在函数或者代码块 {} 外定义,即为全局作用域。不过,在函数或者代码块 {} 内未定义的变量也是拥有全局作用域的(不推荐)。

var course = "es"
// 此处可调用 course 变量
function myFunction() {
    // 函数内可调用 course 变量
}

上述代码中变量 course 就是在函数外定义的,它是拥有全局作用域的。这个变量可以在任意地方被读取或者修改,当然如果变量在函数内没有声明(没有使用 var 关键字),该变量依然为全局变量。

console.log(course) // Uncaught ReferenceError: course is not defined
console.log(window.course) // undefined
function myFunction() {
    console.log(course) // Uncaught ReferenceError: course is not defined
    console.log(window.course) // undefined
    course = "es"
    console.log(course, window.course) // es es
}
myFunction();

以上实例中 course 在函数内,但是拥有全局作用域,它将作为 global 或者 window 的属性存在。

在函数内部或代码块中没有定义的变量实际上是作为 window/global 的属性存在,而不是全局变量。换句话说没有使用 var 定义的变量虽然拥有全局作用域,但是它是可以被 delete 的,而全局变量不可以。

函数作用域:

在函数内部定义的变量,就是局部作用域。函数作用域内,对外是封闭的,从外层的作用域无法直接访问函数内部的作用域!

function bar() {
    var testValue = 'inner'
}
console.log(testValue) // 报错:ReferenceError: testValue is not defined

如果想读取函数内的变量,必须借助 return 或者闭包。
1、return方式:

function bar(value) {
    var testValue = 'inner'
    return testValue + value
}
console.log(bar('fun')) // "innerfun"

2、闭包方式
闭包:闭包是指有权访问另外一个函数作用域中的变量的函数(有两个函数,里面函数访问外面函数变量,导致外面函数的变量不被释放),作用域应用的特殊情况,有两种表现:

  • 函数作为参数被传递
  • 函数作为返回值被返回
function bar(value) {
    var testValue = 'inner'
    var rusult = testValue + value
    function innser() {
        return rusult
    }
    return innser()
}
console.log(bar('fun')) // "innerfun"  函数作为返回值被返回
// 函数作为参数被传递  不适用于此题场景,只作为延伸参考
function print(fn) {
	let a = 200;
	fun();
}
let a = 100;
function fn() {
	console.log(a)
}
print(fn) 

通俗的讲,return 是函数对外交流的出口,而 return 可以返回的是函数,根据作用域的规则,函数内部的子函数是可以获取函数作用域内的变量的。

说到这其实大家会想到嵌套函数的作用域问题,如果 inner 函数再嵌套函数呢?这就涉及到另一个概念:作用域链。
在这里插入图片描述
仔细观察上图,其实不难理解作用域链是什么,因为你可以按照原型链那样去理解。任何一个作用域链都是一个堆栈,首先先把全局作用域压入栈底,再按照函数的嵌套关系一次压入堆栈。在执行的时候就按照这个作用域链寻找变量。

块状作用域

在其他编程语言中,块状作用域是很熟悉的概念,但是在JavaScript中不被支持,就像上述知识一样,除了全局作用域就是函数作用域,一直没有自己的块状作用域。在 ES6 中已经改变了这个现象,块状作用域得到普及。关于什么是块,只要认识 {} 就可以了。

if (true) {
    let a = 1
    console.log(a)
}

在这个代码中, if 后 {} 就是“块”,这个里面的变量就是拥有这个块状作用域,按照规则, {} 之外是无法访问这个变量的。

动态作用域

在 JavaScript 中很多同学对 this 的指向时而清楚时而模糊,其实结合作用域会对 this 有一个清晰的理解。不妨先来看下这段代码:

window.a = 3
function test() {
    console.log(this.a) // 普通函数,this指向window
}
test.bind({
    a: 2
})() // 2
test() // 3

在看看这段代码,写代码的时候就知道 course 就是全局作用域,函数内部的用 var 定义的变量就是函数作用域。这个也就是专业术语:词法作用域。 通俗的讲变量的作用域是在定义时决定而不是执行时决定,也就是说词法作用域取决于源码,通过静态分析就能确定,因此词法作用域也叫做静态作用域。 相反,只能在执行阶段才能决定变量的作用域,那就是动态作用域。

关于作态作用域,面试中经常会被问到this指向问题,this的不同应用场景,如何取值:

  • 作为普通函数(此时的指向是window)
  • 使用call、apply、bind
  • 作为对象方法被调用
  • 在class方法中被调用
  • 箭头函数

1、区分显示的声明与隐式声明(var)

注意!它并不是声明了一个全局变量,而是创建了一个全局对象的属性。

var name = 'muzidigbig';//显示声明 声明变量
name = 'muzidigbig';//隐式声明(为属性赋值)
course = '1'
delete course
console.log(window.course, course) // course is not defined

var course1 = '2'
delete course1
console.log(window.course1, course1) // 2 2

在当前域中声明变量. 如果在方法中声明,则为局部变量(local variable);如果是在全局域中声明,则为全局变量。
而事实上是对属性赋值操作。首先,它会尝试在当前作用域链中解析 name; 如果在任何当前作用域链中找到name,则会执行对name属性赋值; 如果没有找到name,它才会在全局对象(即当前作用域链的最顶层对象,如window对象)中创造name属性并赋值。

console.log(window.name, name); // 1 1
function  person(){
    name = '1';
    console.log(name); //1
}
person();
name = '2';//隐式声明(为全局变量的一个属性)
console.log(window.name, name); // 2 2
function  person(){
    name = '1';
    console.log(window.name, name);//1 1 这里是改变了属性值
}
person();

顶层对象的属性与全局变量挂钩,被认为是JavaScript语言最大的设计败笔之一

顶层对象:在浏览器环境指的是window对象。在Node指的是global对象。ES5中,顶层对象的属性和全局变量是同一个概念。

由此而带来的问题有:
1、污染全局变量,创建的对象全部挂载到window下,会造成window负载过重,不利于维护
2、window对象有实体含义,指的是浏览器的窗口对象。全局变量是一个实体含义的对象,这是不合适的。

2、ES6中let const弥补了顶层对象的属性与全局变量挂钩的设计弊端

let course1 = '2'
console.log(window.course1, course1) // undefined 2

3、在webpack打包编译,可以规避掉顶层对象的问题

在webpack中存在:

  • static文件下的js文件
  • src下的js文件

在src下的js文件中打印window.a会返回undefined,因为src下的js文件经过webpack打包,webpack对打包后的文件做了处理,因此不会有顶层对象的问题

var course1 = '2'
console.log(window.course1, course1) // undefined 2

4、用let定义变量不允许重复声明

var可以重复声明变量

var a = 5
var a = 6
console.log(a) // 6

let不可以重复声明变量

let a = 5
let a = 6
console.log(a) // Uncaught SyntaxError: Identifier 'a' has already been declared

5、let声明的变量不存在变量提升

function foo() {
    console.log(a)
    var a = 5
}
foo() //undefined

等价于

function foo() {
    var a
    console.log(a)
    a = 5
}
foo() //undefined

上述代码中, a 的调用在声明之前,所以它的值是 undefined,而不是 Uncaught ReferenceError。实际上因为 var 会导致变量提升

而对于 let 而言,变量的调用是不能先于声明的,看如下代码:

function foo() {
    console.log(a)
    let a = 5
}
foo()
// Uncaught ReferenceError: Cannot access 'a' before initialization

在上述代码中, a 的调用是在声明之前,因为 let 没有发生变量提升,所有读取 a 的时候,并没有找到,而在调用之后才找到 let 对 a 的定义,所以按照 tc39 的定义会报错。

6、 let声明的变量具有暂时性死区

本质:防止变量在未声明之前使用
只要块级作用域内存在 let 命令,它所声明的变量就绑定在了这个区域,不再受外部的影响。

var a = 5
if (true) {
    a = 6
    let a
}
// Uncaught ReferenceError: Cannot access 'a' before initialization

上面代码中,存在全局变量 a ,但是块级作用域内 let 又声明了一个局部变量 a ,导致后者绑定这个块级作用域,所以在let声明变量前,对 a 赋值会报错。
ES6 明确规定,如果区块中存在 let 和 const 命令,这个区块对这些命令声明的变量,从一开始就形成了封闭作用域。凡是在声明之前就使用这些变量,就会报错。

总之,在代码块内,使用 let 命令声明变量之前,该变量都是不可用的。这在语法上,称为“暂时性死区”

有时“暂时性死区”比较隐蔽,比如:

function foo(b = a, a = 2) {
    console.log(a, b)
}
foo()
// Uncaught ReferenceError: Cannot access 'a' before initialization

上述代码中,b=a,此时a还没有进行定义,foo中代码是从左向右执行的,如果更替为如下代码,则不会报错

function foo(a = 2, b = a) {
    console.log(a, b) // 2 2
}
foo()

7、let 声明的变量拥有块级作用域

let实际上为 JavaScript 新增了块级作用域

{
    let a = 5
}
console.log(a) // undefined

a 变量是在代码块 {} 中使用 let 定义的,它的作用域是这个代码块内部,外部无法访问。

我们再看一个项目中很常见的 for 循环:

for (var i = 0 i < 3 i++) {
    console.log('循环内:' + i) // 0、1、2
}
console.log('循环外:' + i) // 3

如果改为 let 会怎么样呢?

for (let i = 0 i < 3 i++) {
    console.log('循环内:' + i) // 0、1、2
}
console.log('循环外:' + i) // ReferenceError: i is not defined

继续看下面两个例子的对比,这时 a 的值又是多少呢?

if (false) {
    var a = 5
}
console.log(a) // undefined
if (false) {
    let a = 5
}
console.log(a)
// Uncaught ReferenceError: a is not defined

面试题例题:
请问 i 的值是多少?

for (var i = 0; i < 3; i++) {
    setTimeout(function() {
        console.log(i)
    })
}
// 3、3、3

答案是3次3。
但我希望的值是0、1、2,也就是每次保存住循环时候 i 的值,应该如何做呢?

方案1:闭包

for (var i = 0;i < 3; i++) {
    (function(j) {
        setTimeout(function() {
            console.log(j)
        })
    })(i)
}

方案2:使用let
这种方式其实通过babel转化之后,依然是闭包的形式
babel:https://www.babeljs.cn/repl

for (let i = 0 i < 3 i++) {
    setTimeout(function() {
        console.log(i)
    })
}

8、const定义常量

ES5 中可以使用 Object.defineProperty() 来实现定义常量:

Object.defineProperty(window, 'PI', {
    value: 3.14,
    writable: false, // 不可写的
})
console.log(PI) // 3.14
PI = 5
console.log(PI) // 3.14

const 除了具有 let 的块级作用域和不会变量提升外,还有就是它定义的是常量,在用 const 定义变量后,我们就不能修改它了,对变量的修改会抛出异常。

const PI = 3.1415
console.log(PI)
PI = 5
console.log(PI)
// Uncaught TypeError: Assignment to constant variable.

const 声明的变量必须进行初始化,不然会抛出异常 Uncaught SyntaxError: Missing initializer in const declaration。

const重点:

const obj = {
    name: 'xiecheng',
    age: 34
}
obj.school = 'imooc'
console.log(obj)
// {name: "xiecheng", age: 34, school: "imooc"}

这种情况中 const 定义的 obj 是可以被改变的
在这里插入图片描述
这是因为:基本数据类型存储在 栈内存 中,引用数据类型存储在 堆内存 中然后在栈内存中保存 引用地址 。

const 实际上保证的并不是变量的值不得改动,而是变量指向的那个内存地址所保存的数据不得改动。

如何让对象或者数组这种引用数据类型也不被改变呢?

Object.freeze(obj)

Object.freeze() 只是浅层冻结,只会对最近一层的对象进行冻结,并不会对深层对象冻结。

二、解构赋值

数组的解构赋值

解构赋值重点是在赋值,赋值的元素是要拷贝出来赋值给变量,赋值的元素本身是不会被改变的。

let arr = [1, 2, 3]
let a = arr[0]
let b = arr[1]
let c = arr[2]

等价于

let [a, b, c] = [1, 2, 3]
1. 赋值元素可以是任意可遍历的对象
let [a, b, c] = "abc" 
let [one, two, three] = new Set([1, 2, 3])
console.log([a, b, c], [one, two, three], a)
// ["a", "b", "c"] [1, 2, 3] "a"

let [a, b, c] = "abc" // ["a", "b", "c"]
let [one, two, three] = new Set([1, 2, 3])
console.log([a, b, c], [one, two, three], a)

let [a, b, [c, d]] = [1, 2, [3, 4]]
console.log(a, b, c, d) // 1 2 3 4

let [a, b, [c]] = [1, 2, [3, 4]]
console.log(a, b, c) // 1 2 3

let [a, b, c] = [1, 2, [3, 4]]
console.log(a, b, c) // 1 2 [3, 4]

let [a, b, c, d] = [1, 2, [3, 4]]
console.log(a, b, c, d) // 1 2 [3, 4] undefined

let [a, b, c, d = 5] = [1, 2, [3, 4], 6]
console.log(a, b, c, d) // 1 2 [3, 4] 6  解构赋值是惰性的,以传入的赋值为准
2.左边的变量

被赋值的变量还可以是对象的属性,不局限于单纯的变量。

let user = {}
[user.firstName, user.secondName] = 'Kobe Bryant'.split(' ')
console.log(user.firstName, user.secondName) // Kobe Bryant
3.循环体

解构赋值在循环体中的应用,可以配合 entries 使用。

let user = {
  name: 'John',
  age: 30
}

// loop over keys-and-values
for (let [key, value] of Object.entries(user)) {
  console.log(`${key}:${value}`) // name:John, then age:30
}

当然,对于 map 对象依然适用:

var obj = { foo: "bar", baz: 42 };
var map = new Map(Object.entries(obj));
for (let [key, value] of map) {
    console.log(`${key}:${value}`) // name:John, then age:30
}
3.可以跳过赋值元素

如果想忽略数组的某个元素对变量进行赋值,可以使用逗号来处理。

// second element is not needed
let [name, , title] = ['John', 'Jim', 'Sun', 'Moon']

console.log( title ) // Sun
4.rest 参数
let [name1, name2, ...rest] = ["Julius", "Caesar", "Consul", "of the Roman Republic"]

console.log(name1) // Julius
console.log(name2) // Caesar

// Note that type of `rest` is Array.
console.log(rest[0]) // Consul
console.log(rest[1]) // of the Roman Republic
console.log(rest.length) // 2

我们可以使用 rest 来接受赋值数组的剩余元素,不过要确保这个 rest 参数是放在被赋值变量的最后一个位置上。

5. 默认值

当然你也可以给变量赋予默认值,防止 undefined 的情况出现:

let [firstName, surname='Mia'] = []

console.log(firstName) // undefined
console.log(surname) // Mia

对象解构赋值

1.基本用法
let options = {
  title: "Menu",
  width: 100,
  height: 200
}

let {width, title, height} = options // 顺序可随意

在这个结构赋值的过程中,左侧的“模板”结构要与右侧的 Object 一致,但是属性的顺序无需一致。

如果不想这么写或者想使用其他的变量名,可以自定义的,如下:

let {width: w, height: h, title} = options
2.默认值
let options = {
  title: "Menu"
}

let {width, height = 200, title} = options

console.log(title)  // undefined
console.log(width)  // 100
console.log(height) // 200
3.rest 运算符

如果我们想象操作数组一样,只关心指定的属性,其他可以暂存到一个变量下,这就要用到 rest 运算符了

let options = {
  title: "Menu",
  height: 200,
  width: 100
}

let {title, ...rest} = options

// now title="Menu", rest={height: 200, width: 100}
console.log(rest.height)  // 200
console.log(rest.width)   // 100
4.嵌套对象

如果一个 Array 或者 Object 比较复杂,它嵌套了 Array 或者 Object,那只要被赋值的结构和右侧赋值的元素一致就好了。

let options = {
  size: {
    width: 100,
    height: 200
  },
  items: ["Cake", "Donut"],
  extra: true    // something extra that we will not destruct
}

// destructuring assignment on multiple lines for clarity
let {
  size: { // put size here
    width,
    height
  },
  items: [item1, item2], // assign items here
  title = 'Menu' // not present in the object (default value is used)
} = options

console.log(title)  // Menu
console.log(width)  // 100
console.log(height) // 200
console.log(item1)  // Cake
console.log(item2)  // Donut

字符串解构赋值

可以当做是数组的解构:

let str = 'imooc'
let [a, b, c, d, e] = str
console.log(a, b, c, d, e)

参数的结构赋值

function foo([a, b, c]) {
    console.log(a, b, c)
}
let arr = [1,2,3]
foo(arr)
function foo({name, age, school = 'beida'}) {
    console.log(name, age, school)
}
let obj = {
    name: 'mia',
    age: 18
}
foo(obj)

函数返回值的结构赋值

function foo() {
    return {
        name: 'mia',
        age: 18
    }
}
let {name, age} = foo(); // mia 18

三、数组遍历

ES5 中数组遍历方式

1.for循环
for (let i = 0; i < arr.length; i++) {
    console.log(arr[i])
}
2.forEach()没有返回值,只是针对每个元素调用func

arr.forEach(callback(currentValue[, index[, array]]) {
// execute something
}[, thisArg]);

  • currentValue:必需。当前元素
  • index:可选。当前元素的索引值。
  • array:可选。当前元素所属的数组对象。
  • thisArg:可选。传递给函数的值一般用 “this” 值。如果这个参数为空, “undefined” 会传递给 “this” 值

forEach弊端:
1、方法不能使用break,continue语句跳出循环,它会抛出异常。
2、不能使用return从函数体返回,不会抛出异常。
3、对于空数组不会执行回调函数

arr.forEach(function(elem, index, array) {
    if (arr[i] == 2) {
        continue
    }
    console.log(elem, index) // Uncaught SyntaxError: Illegal continue statement: no surrounding iteration statement
})

上面代码会抛出异常

[1, 2, 3, 4, 5].forEach(function(i) {
    if (i === 2) {
        return;
    } else {
        console.log(i)
    }
})

上面这段代码的"本意"是从第一个元素开始遍历,遇到数组项 2 之后就结束遍历,不然打印出所遍历过的数值项。可是,事实让你大跌眼镜,因为它的输出是 1, 3, 4, 5。

3.map() 返回新的数组,每个元素为调用func的结果

let newArray = arr.map(callback(currentValue[, index[, array]]) {
// return element for newArray, after executing something
}[, thisArg]);

let result = arr.map(function(value) {
    value += 1
    console.log(value)
    return value
})
console.log(arr, result)
4.filter() 返回符合func条件的元素数组

let newArray = arr.filter(callback(currentValue[, index[, array]]) {
// return element for newArray, if true
}[, thisArg]);

  • filter() 不会对空数组进行检测。
  • filter() 不会改变原始数组。
let result = arr.filter(function(value) {
    console.log(value)
    return value == 2
})
console.log(arr, result)
5.some() 返回boolean,判断是否有元素符合func条件

arr.some(callback(element[, index[, array]])[, thisArg])

let result = arr.some(function(value) {
    console.log(value)
    return value == 4
})
console.log(arr, result)
6.every() 返回boolean,判断每个元素都符合func条件

arr.every(callback(element[, index[, array]])[, thisArg])

注意:和some的区别是every判断的是每一个元素

let result = arr.every(function(value) {
    console.log(value)
    return value == 2
})
console.log(arr, result)

every弊端:
every 的代码块中不能使用 break、continue,它会抛出异常。

7.reduce() 接收一个函数作为累加器

reduce() 方法接收一个函数作为累加器,数组中的每个值(从左到右)开始缩减,最终计算为一个值。

reduce() 可以作为一个高阶函数,用于函数的 compose。

arr.reduce(callback( accumulator, currentValue, [, index[, array]] )[, initialValue])

注意: reduce() 对于空数组是不会执行回调函数的。

  • accumulator:必需。初始值, 或者计算结束后的返回值。
  • currentValue:必需。当前元素。
  • index:可选。当前元素的索引。
  • array:可选。当前元素所属的数组对象。
  • initialValue:可选。传递给函数的初始值
let sum = arr.reduce(function(accumulator, currentValue, index, array) {
    return accumulator + currentValue
}, 0)
console.log(sum) // 10
let max = arr.reduce(function(accumulator, currentValue) {
    return Math.max(accumulator, currentValue)
})
console.log(max) // 4
let res = arr.reduce(function(accumulator, currentValue) {
    accumulator.indexOf(currentValue) == -1 && accumulator.push(currentValue)
    return accumulator
}, [])
console.log(res) //[1,2,3,4]
8.for…in 可以遍历数组??不可以
for (var index in array) {
    console.log(array[index]);
}

for…in不能用于遍历数组。
for…in代码块中不能有 return,不然会抛出异常。

为什么不可以呢?因为如果给数组添加自定义属性,这里使用for… in…就会把自定义的属性也遍历出来。

let arr = [1, 2, 3, 4]
Array.prototype.test = function() {
    console.log('aa')
}
for (let i in arr) {
    console.log(i)  // 1 2 3 4 test,显然这里的test是不应该被打印出来的
} 

ES6中的遍历数组方法

1. for…of

for (variable of iterable) {}
注意:
1、这里是iterable,意味着万物皆可遍历(数组、对象、集合)等

2、for…of是支持 break、continue、return的,所以在功能上非常贴近原生的 for。

上面这个伪代码,of 后面是 iterable 既不是 for 循环规定的 array,也不是 for…in 规定的 Object,而是 iterable。不要小瞧这个功能,因为在 ES6 中允许开发者自定义遍历,换句话说任何数据结构都可以自定义一个遍历,同样可以遍历promise,这个遍历是不能被 for、for…in 理解和实现的。

for (let val of [1, 2, 3]) {
    console.log(val);
}
// 1,2,3
let arr = ['item1', 'item2', 'item3', 'item4']
for (let item of arr) {
    console.log(item)
} // 'item1', 'item2', 'item3', 'item4'
for (let item of arr.values()) {
    console.log(item)
} // 'item1', 'item2', 'item3', 'item4'
for (let item of arr.keys()) {
    console.log(item)
} // 0 1 2 3
for (let [index, item] of arr.entries()) {
    console.log(index, item)
} // 0 'item1' 1 'item2' 2 'item3' 3 'item4'

for…of是支持 break、continue、return的,所以在功能上非常贴近原生的 for。

ES6中的其他数组方法

1.Array.from()
扩展:伪数组/类数组
1.伪数组特征
  • 具有length属性;
  • 按索引方式存储数据;
  • 不具有数组的push()、pop()等方法;
2.es5中对伪数组的转化方式

数组是开发中经常用到的数据结构,它非常好用。在 JavaScript 的世界里有些对象被理解为数组,然而缺不能使用数组的原生 API,比如函数中的 arguments、DOM中的 NodeList等。当然,还有一些可遍历的对象,看上去都像数组却不能直接使用数组的 API,因为它们是伪数组(Array-Like)。常见的有:

let div1 = document.getElementsByTagName('div');
let div2 = document.getElementsByClassName('test');
let div3 = document.querySelectorAll('test');
console.log(div1, div2, div3); // HTMLCollection HTMLCollection  NodeList(节点列表)

// 我们可以通过instanceof方法(左面的元素是不是右边的实例)来判断是不是真的数组
console.log(div1 instanceof Array) // false

要想对这些对象使用数组的 API 就要想办法把它们转化为数组,传统的做法是这样的:

let args = [].slice.call(arguments);
let imgs = [].slice.call(document.querySelectorAll('img'));
3.es6中对伪数组的转化方式

基本原理是使用 call 将数组的 api 应用在新的对象上,换句话说是利用改变函数的上下文来间接使用数组的 api。在 ES6 中提供了新的 api 来解决这个问题,就是 Array.from,代码如下:

let args = Array.from(arguments);
let imgs = Array.from(document.querySelectorAll('img'));
let arrLike = {
    0: 'a',
    1: 'b',
    2: 'c',
    length: 3
}
let arr1 = Array.from(arrLike);
console.log(arr1, arrLike) // 输出结果如下,可以看出已经转化为了真的数组

在这里插入图片描述

敲重点:Array.from的其他用法

除了常用的将伪数组转化为数组的方法之外

Array.from(arrayLike[, mapFn[, thisArg]])

参数含义必选
arrayLike想要转换成数组的伪数组对象或可迭代对象Y
mapFn如果指定了该参数,新数组中的每个元素会执行该回调函数N
thisArg可选参数,执行回调函数 mapFn 时 this 对象N

看了这几个参数至少能看到 Array.from 还具备 map 的功能,比如我们想初始化一个长度为 5 的数组,每个数组元素默认为 1,之前的做法是这样的:

let arr = Array(6).join(' ').split('').map(item => 1)
// [1,1,1,1,1]

这样写虽然也能实现,但是用起来比较繁琐,使用 Array.from 就会简洁很多。

Array.from({
    length: 5
}, function() {
    return 1
})

当然是用es5的方法也可以实现:

new Array(5).fill(1)
2.Array.of()

Array.of() 方法创建一个具有可变数量参数的新数组实例,而不考虑参数的数量或类型。

Array.of(element0[, element1[, …[, elementN]]])

参数含义必选
elementN任意个参数,将按顺序成为返回数组中的元素Y

Array.of() 和 Array 构造函数的区别:
Array.of(7) 创建一个具有单个元素 7 的数组,而 Array(7) 创建一个长度为7的空数组(注意:这是指一个有7个空位(empty)的数组,而不是由7个undefined组成的数组)。

Array.of(7); // [7]
Array.of(1, 2, 3); // [1, 2, 3]

Array(7); // [ , , , , , , ]
Array(1, 2, 3); // [1, 2, 3]

所以数组拼接除了concat之外,也可以用Array.of拼接

let arr1 = [1, '3'];
let arr2 = [{name: 'mia', age: 18}, ['9', false]];
let arr3 = Array.of(arr1, arr2); // [[1, '3'], [{name: 'mia', age: 18}, ['9', false]]]
let arr4 = Array.of(...arr1, ...arr2); // [1, '3', {name: 'mia', age: 18}, ['9', false]]
let arr5 = arr1.concat(arr2); // [1, '3', {name: 'mia', age: 18}, ['9', false]]
3.Array.prototype.fill()

fill() 方法用一个固定值填充一个数组中从起始索引到终止索引内的全部元素。不包括终止索引。

arr.fill(value[, start[, end]])

参数含义必选
value用来填充数组元素的值Y
start起始索引,默认值为0N
end终止索引,默认值为 this.lengthN
let array = [1, 2, 3, 4]
array.fill(0, 1, 2) // [1,0,3,4]
array.fill('h') // ['h', 'h', 'h', 'h']

这个操作是将 array 数组的第二个元素(索引为1)到第三个元素(索引为2)内的数填充为 0,不包括第三个元素,所以结果是 [1, 0, 3, 4]

4.Array.prototype.find()

find() 方法返回数组中满足提供的测试函数的第一个元素的值,否则返回 undefined。

arr.find(callback[, thisArg])

参数含义必选
callback在数组每一项上执行的函数,接收 3 个参数,element、index、arrayY
thisArg执行回调时用作 this 的对象N
let array = [5, 12, 8, 130, 44];

let found = array.find(function(element) {
    return element > 10;
});

console.log(found);
// 12
5.Array.prototype.findIndex()

findIndex()方法返回数组中满足提供的测试函数的第一个元素的索引。否则返回-1。其实这个和 find() 是成对的,不同的是它返回的是索引而不是值。

arr.findIndex(callback[, thisArg])

参数含义必选
callback在数组每一项上执行的函数,接收 3 个参数,element、index、arrayY
thisArg执行回调时用作 this 的对象N
let array = [5, 12, 8, 130, 44];

let found = array.find(function(element) {
    return element > 10;
});

console.log(found);
// 1
6.Array.prototype.copyWithin()

在当前数组内部,将指定位置的成员复制到其他位置(会覆盖原有成员),然后返回当前数组。也就是说,使用这个方法,会修改当前数组

arr.copyWithin(target, start = 0, end = this.length)

参数含义必选
target从该位置开始替换数据。如果为负值,表示倒数Y
start从该位置开始读取数据,默认为 0。如果为负值,表示从末尾开始计算N
end到该位置前停止读取数据,默认等于数组长度。如果为负值,表示从末尾开始计算N
let arr = [1, 2, 3, 4, 5]
arr.copyWithin(1, 3)
console.log(arr) // [1, 4, 5, 4, 5]
7. 扩展:arr.values()、 arr.keys()、arr.entries()、es6中includes和 原生的indexOf区别

.values() : 取的是值
.keys() : 取的是下标值
.entries() : 既显示值又显示下标

es6中includes和 原生的indexOf区别:
1、返回值不同,includes返回true/false,indexOf找不到返回-1
2、indexOf不可以判断NaN,includes可以

let arr = [1, NaN, 2];
let result1 = arr.includes(NaN);
let result2 = arr.indexOf(NaN);
console.log(result1, result2) // true -1

string.includes(searchvalue, start)

参数含义必选
searchvalue要查找的字符串Y
start从该位置开始读取数据,默认为 0。如果为负值,表示从末尾开始计算N
const arr1 = ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', NaN]
console.log('%s', arr1.includes('d', 1))   // true
console.log('%s', arr1.includes('k', -1))  // false

文章仅作为学习使用,如有侵权请告知删除,谢谢

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值