函数

79 阅读6分钟

开启掘金成长之旅!这是我参与「掘金日新计划 · 2 月更文挑战」的第 5 天

for in for of区别

for in适合遍历对象,会遍历继承的或者原型链上的key,取到的是key

for of适合遍历数组,es6新增,只会遍历当前对象,取到的是value

若for of想遍历对象,判断该对象是否是类数组,是就转为数组,不是就添加obj[Symbol.iterator]属性再指向一个迭代器。

判断是否为数组

Array.isArray(obj)

obj instanceof Array

Object.prototype.toString.call(obj).slice(8,-1)==='Array'

Array.prototype.ifPrototypeOf(obj)

类数组转数组

[...arguments]

Array.form(obj)

Object.prototype.slice.call(obj)

Object.prototype.splice.call(obj,0)

Object.prototype.concat.call([],obj)

数组原生方法

改变原数组

  • pop()尾部删除,无参数,返回被删除的元素

  • shift()头部删除,无参数,返回被删除的元素

  • unshift()头部添加,参数是被添加的元素,返回新数组的长度

  • push()尾部添加,参数是被添加的元素,返回新数组长度

  • splice()删除或者在删除的位置添加元素,(index,count,item1,item2...),

参数个数为1在index处分割数组,返回分割线index后面的数组

参数个数为2,从index开始删除count个元素,返回被删除的元素

参数个数大于2在index处删除count个元素并添加元素,返回被删除的元素

  • reverse()翻转数组,无参数,返回翻转后的数组

  • sort()排序,可以传入函数,自定义排序规则

不改变原数组

  • slice()截取数组,返回截取后的数组

参数为空,深拷贝原数组

参数个数为1,从传入的index截取至最后一个

参数个数为2(start end)包括start不包括end

  • toString()、toLocaleString()、join("")转字符串,toLocaleString()转日期字符串

  • indexOf()查找元素第一次出现的位置,找到返回元素的index,没找到返回-1

  • lastIndexOf()查找元素最后一次出现的位置,找到返回元素的index,没找到返回-1

  • includes()返回布尔值,是否包含某个元素

  • reduce()传函数,累加器求和,从左到右

  • reduceRight()从右到左

  • map()返回数组,通常用来添加属性,改变属性。可以链式调用。

基本类型不改变,引用类型会改变原数组

forEach()无返回值,通常将item用作其他地方。

原数组中是基本数据类型,用forEach会拷贝原数组的指针和值,完全独立不影响原数组。若想修改需要加入第二个参数index,arr[index修改]

原数组中是引用数据类型obj,用forEach会拷贝原数组的指针也就是在栈中的地址,直接修改整个对象不影响原数组,但是修改对象的某个属性会影响原数组

  • find()返回第一个符合条件的值
  • findIndextOf()返回第一个符合条件的值的索引
  • some()一true就true
  • every()都true才true
  • filter()传函数,返回数组包含符合条件的元素.可以链式调用

遍历数组方式

for(let i=0;i<arr.length;i++){ arr[i] }

for(let i of arr){ i是下标对应的值 }

arr.forEach((item)=>{})

newArr = arr.map((item)=>{})

作用域、作用域链、闭包

全局作用域:最外层函数及最外层函数外定义的变量 未定义直接赋值的变量,一般不推荐,造成变量污染

函数作用域:函数内部定义的变量,外部无法访问,除了闭包

块级作用域:let const定义的变量,只在代码块中使用,不能重复命名。let没有变量提升。一般用于for循环,每次循环 都是单独的块级作用域不会被覆盖

作用域链:对于函数中的自由变量,若在当前作用域找不到,会向上查找直到全局作用域也没有就是undefined,但是外层不能访问内层的。

闭包:有权访问另一个函数作用域中的变量的函数。

对象不会构成单独的作用域,箭头函数向上查找this不会找到上级对象

执行上下文

js执行分为创建阶段、执行阶段

创建阶段:词法分析、语法分析、作用域规则确定

执行阶段:创建执行上下文、执行函数确定this、垃圾回收

js引擎通过执行栈管理执行上下文,全局执行上下文、函数执行上下文

所以作用域沿着创建该函数的地方向上找、this执行的时候谁最后调用就指向谁

this

new>call apply bind>作为对象方法调用>函数调用

用new时候this指向window或者构造函数

new

创建一个空对象,将constructor.prototype指向空对象的

执行将构造函数的this指向新对象

判断返回值是引用类型就返回res,不是就返回新对象

function news(constructor, params) {
    let args = Array.from(arguments)
    let cons = args.shift()
    let obj = Object.create(cons.prototype)
    let res = cons.apply(obj, args)
    return typeof res ==='object'&&res!==null?res:obj
}

参数:call apply bind第一个参数都是this,call bind后面参数要列举出来,apply第二个参数是剩余参数的集合[],call apply会直接执行函数,bind会保留旧this,arguments,return函数进行参数拼接

call

判断调用call的是否为函数,判断传入的新this,若没有设为window

新this上添加symbol属性key,将旧this作为方法放入新this中

执行该方法携带参数拿到返回值

删除symbol属性

return 执行的结果

function call(context) {
    if (typeof this !== 'function') {
        console.error();
    }
    context = context || window
    let key = Symbol()
    context[key] = this
    let args = [...arguments].slice(1)
    let res = context[key](...args)
    delete context[key]
    return res
}

apply

和call一样,就是执行的时候,参数取...arguments[1]是个集合

bind

参数和call一样从第一个截取,保留新this 旧this和参数,return函数,判断新this instanceof Fn?this:旧this,拼接参数

function bind(context) {
    if (typeof this !== 'function') {
    }
    context = context || window
    let fn = this
    let args = [...arguments].slice(1)
    return function Fn() {
        fn.apply(this instanceof Fn?this:context,args.concat(...arguments))
    }
}

原型、原型链

每个构造函数都有一个原型prototype,这个原型是对象里面存放一些属性和方法。这些属性和方法都会被构造函数创建的实例共享,当这个实例对象在自身找不到对应的属性和方法时候会去它的原型找也就是构造函数的原型通过__proto__或者Object.getProtoTypeOf(obj),若其原型的原型没有,就会去prototype的原型上找,找到Object.prototype,Object.prototype的原型是null,寻找的过程也就是所说的原型链,instanceOf就是这个原理去查找对象的原型链上是否存在构造函数的prototype

在这里构造函数Fn比较特殊,Fn生了自己和Object。Fn.prototype.__proto__是Object.prototype最终指向null

Object.__proto是Fn.prototype