前端基础面试题·第三篇——JavaScript(其二)

1.深浅拷贝

1.浅拷贝
浅拷贝会创建一个新的对象,这个对象有着原始对象属性值的一份精确拷贝。如果属性是基本类型,拷贝的就是基本类型的值,如果属性是引用类型,拷贝就是改引用类型的地址。

    // 常见的浅拷贝
    1.Object.assign({},obj) //对象浅拷贝
    assign⽅法可以⽤于处理数组,不过会把数组视为对象,⽐如这⾥会把⽬标数组视为是属性为012的对象,所
    以源数组的01属性的值覆盖了⽬标对象的值。
    2.Array.concat({}) // 数组浅拷贝
    3.Array.slice() // 数组浅拷贝
    4.扩展运算符

2.深拷贝
深拷贝会创建一个新的对象,这个对象有着原始对象属性值的一份完整拷贝。如果属性是基本类型,拷贝的就是基本类型的值,如果属性是引用类型,就会将引用数据类型复制一份存到内存中,将其地址值赋值给新对象。

    // 常见的深拷贝
    1.JSON.parse(JSON.stringfy(obj)) // 对象深拷贝
    2.递归拷贝
    3.loadsh第三方库

2.freeze属性

Object.freeze()方法可以冻结一个对象,冻结指的是不能向这个对象添加新的属性,不能修改其已有属性的值,不能删除已有属性,并且返回当前对象。
这⾥是关于属性的四个基本类型

  1. [[Configurable]] :表示属性是否可以通过 delete 删除并重新定义,是否可以修改它的特性,以及是否可以把它改为访问器属性。默认为 true 。
  2. [[Enumerable]] :
    表示属性是否可以通过for-in循环返回。默认为 true 。
  3. [[Writable]] :
    表示属性的值是否可以被修改。默认为 true 。
  4. [[Value]] :
    表示属性实际的值。默认为 undefined 。
    当我们显式的添加⼀个属性之后,它的值就会被设置到 [[Value]] 中,⽽ [[Writable]]表示我们能否读这个值进⾏修
    改,如果设置为 false ,那么当我们修改这个值的时候回就会报错,同理其他的属性也是⼀样的。⽽如果我们要修
    改属性的类型的值的时候 ,是使⽤ Object.defineProperty()⽅法 来进⾏修改的。

freeze作用:

  • 设置 Object.preventExtension(),禁⽌添加新属性 ( 绝对存在 )
  • 设置 Writable 为 false,禁⽌修改 ( 绝对存在 )
  • 设置 Configurable 为 false,禁⽌配置 ( 绝对存在 )
  • 禁⽌更改访问器属性 ( getter 和 setter )

3.函数

1.函数声明
函数声明:function 函数名(){}
函数表达式:var 函数名 = function(){}
箭头函数: 函数名 = () => {}
2. 3种声明函数的区别

  1. 函数声明:函数声明会提升,在代码执行前被提前加载。
  2. 函数表达式:函数表达式不会提升,只有在代码执行到它的时候才会被加载。
  3. 箭头函数:箭头函数不会提升,在代码执行到它的时候才会被加载。

匿名函数
匿名函数:没有名字的函数,只能通过变量来调用。没有被显示进⾏赋值操作的函数。 使⽤场景——多作为⼀个参数传⼊另⼀个函数中
函数⾃执⾏,其实是匿名函数的⼀种应⽤
回调函数
回调函数:⼀个函数作为参数传⼊到另一个函数中,这个函数就是回调函数。使⽤场景——多作为⼀个参数传⼊另⼀个函数中
高阶函数
高阶函数:⼀个函数 接收另⼀个函数作为参数或返回另⼀个函数

特征
函数是第⼀等公⺠
所谓"第⼀等公⺠"(first class),是函数与其他数据类型⼀样,处于平等地位,可以赋值给其他变量,也可以作为
参数,传⼊另⼀个函数,或者作为别的函数的返回值
只⽤"表达式",不⽤"语句"
“表达式”(expression)是⼀个单纯的运算过程,总是有返回值;“语句”(statement)是执⾏某种操作,没有返
回值。函数式编程要求,只使⽤表达式,不使⽤语句。每⼀步都是单纯的运算,都有返回值
函数式编程期望⼀个函数有输⼊,也有输出
纯函数
即:只要是同样的参数传⼊,返回的结果⼀定是相等的

4.函数柯里化

函数柯里化:
这里有一个函数(假设叫做addCurry),接收函数A作为参数,运⾏后返回⼀个新的函数。并且这个新的函数能够处理函数A的剩余参数

    let addCurry = curry1((a, b) => a + b);
    console.log(addCurry()(11)(1));
    function curry1(fn) {
    let judge = (...args) => {
    if (args.length === fn.length) {
    return fn.call(this, ...args);
    }
    //获取偏函数,返回包装器,重新组装参数并传⼊
    return (...arg) => judge(...arg, ...args)
    }
    return judge;
    }

5.函数的length

length属性返回函数接收的参数个数,不包括rest参数。 这里不包括在参数定义位置赋了默认值的形参,同时也不包括剩余参数。

  1. 形参个数
    来看看下⾯这个例⼦
function fn1 () {}
function fn2 (name) {}
function fn3 (name, age) {}
console.log(fn1.length) // 0
console.log(fn2.length) // 1
console.log(fn3.length) // 2

可以看出,function有多少个形参,length就是多少。但是事实真是这样吗?继续往下看
2. 默认参数
如果有默认参数的话,函数的length会是多少呢?

function fn1 (name) {}
function fn2 (name = '林三⼼') {}
function fn3 (name, age = 22) {}
function fn4 (name, aaa, age = 22, gender) {}
function fn5(name = '林三⼼', age, gender, aaa) {}
console.log(fn1.length) // 1
console.log(fn2.length) // 0
console.log(fn3.length) // 1
console.log(fn4.length) // 2
console.log(fn5.length) // 0

上⾯可以看出,function的length,就是第⼀个具有默认值之前的参数个数
3. 剩余参数
在函数的形参中,还有剩余参数这个东⻄,那如果具有剩余参数,会是怎么算呢?

function fn1(name, ...args) {}
console.log(fn1.length) // 1

6.普通函数与箭头函数的区别

  1. 普通函数在ES6之前就有,箭头函数是在ES6中1引入
  2. 普通函数中存在this关键字,箭头函数中不存在
  3. 普通函数中存在arguments对象,箭头函数中不存在
  4. 普通函数存在原型,箭头函数不存在
  5. 由于普通函数存在原型,因此可以作为构造函数使用,箭头函数不行
  6. 普通函数存在原型链,箭头函数不存在原型链
  7. 普通函数可以定义为生成器函数,又yield关键字,箭头函数不行

7.构造函数

1.构造函数
在js中,通过一个new 关键字来创建对象的函数称为构造函数,也就是初始化一个实例对象,构造函数的命名一般都是首字母大写

构造函数创建对象的过程即本文上面提到的new关键字创建对象的过程

2.构造函数的返回值
构造函数中,一般不允许自己写返回值,当在函数中返回一个基本数据类型时,返回值无效,并不会返回给外面接收变量,但当返回一个引用数据类型时,这个引用数据类型将会覆盖本次创建的对象,从而会导致构造函数没有效果,因此不建议在构造函数中返回值

8.执行上下文

在js代码运行时,会产生三种执行上下文,分别是全局上下文,函数上下文和eval上下文,每种执行上下文都有三个重要属性:变量对象,作用域链,this
1.全局上下文
全局上下文在js代码执行之前就已经存在了,当js代码开始执行时,会首先进入全局上下文,全局上下文中包含有全局对象和this,在全局上下文中,作用域为全局作用于,this指向全局对象(在浏览器端指向window)
2.函数上下文
当函数被调用时,会创建函数上下文,函数上下文包含有变量对象,作用域链,this,函数上下文中的this指向调用函数的对象,如果没有对象调用函数,那么this指向全局对象(在浏览器端指向window)
3.eval上下文
eval函数执行时,会创建eval上下文,eval上下文包含有变量对象,作用域链,this,eval上下文中的this指向全局对象(在浏览器端指向window)

9.闭包

简单来说,闭包就是在一个函数中返回另一个函数,而返回的函数使用到了原函数中定义的变量,这样导致原函数执行完之后会残留一个变量被返回函数一直引用,无法被垃圾回收机制回收,同时在原函数外部根本除了返回函数外无法被其他方式访问到,因此就形成了闭包
闭包 = 函数 + 函数能够访问的⾃由变量。
本质上,闭包就是一个定义在上级作用域中的变量,在下级作用域中引用到了这个变量,本应在上级作用域结束时释放的变量因为间接引用没有被释放,因此就形成了闭包
作用

  1. 封装变量,方式变量污染
  2. 实现变量结果缓存,避免重复计算
  3. 实现私有变量,防止外部访问

运用

  1. 防抖节流
    function debounce(fn, delay = 300) {
    let timer; //闭包引⽤外界变量
    return function () {
    consr args = arguments;
    if (timer) {
    clearTimeout(timer);
    }
    timer = setTimeout(() => {
    fn.apply(this, args);
    }, delay);
    };
    }
  1. 模拟块级作用域
    function OutPutNum(cnt) {
    
    (function () {
    for (let i = 0; i < cnt; i++) {
    alert(i);
    }
    
    })();
    
    alert(i);
    }

10.this

this指向在函数被调⽤时确定,即执⾏上下⽂被创建时确定。函数执⾏过程中,⼀旦this指向被确定就不可更改。
在⼀个函数上下⽂中,this由调⽤者提供,由调⽤函数的⽅式来决定。如果调⽤者函数,被某⼀个对象所拥有,那
么该函数在调⽤时,内部的this指向该对象。如果函数独⽴调⽤,那么该函数内部的this,则指向undefined。但
是在⾮严格模式中,当this指向undefined时,它会被⾃动指向全局对象

1.严格模式
全局:
在严格模式中,全局上下⽂中的this值是undefined,⽽不是指向全局对象。
函数:在严格模式中,⾮箭头函数内部的this值是undefined,⽽不是指向全局对象或undefined。
对象方法:在严格模式中,对象⽅法中的this指向对象。
2.非严格模式
全局:在非严格模式中,全局上下⽂中的this值是全局对象。
函数:在非严格模式中,⾮箭头函数内部的this值是全局对象。
对象方法:在非严格模式中,对象⽅法中的this指向调用该方法的对象。
3.箭头函数
箭头函数中没有自己的this,沿用了上层作用域的this
作为⽅法的箭头函数 this 指向当前被定义变量的上下⽂环境。
箭头函数的this 为定义时所在的 this,不绑定this (因为箭头函数没有Constructor这个内部槽),会捕获其所在上下
⽂的 this 作为⾃⼰的 this。(箭头函数的this就是它在外⾯的第⼀个不是箭头函数的函数的this)**
4.修改this指向
bind():改变this指向,返回一个新函数原函数的副本,并绑定this指向bind的第一个参数,接受的第一个参数是需要绑定的this,其余参数是原函数的参数
call():改变this指向,立即执行原函数,并绑定this指向call的第一个参数,接受的第一个参数是需要绑定的this,其余参数是原函数的参数
apply():改变this指向,立即执行原函数,并绑定this指向apply的第一个参数,接受的第一个参数是需要绑定的this,接受的第二个参数是原函数的参数

手写以上三个函数

    Function.prototype.myCall = function (obj,...args) {
        obj.fn = this
        const res = obj.fn(...args)
        delete obj.fn
        return res
    }
    Function.prototype.myApply = function (obj,args) {
        obj.fn = this
        const res = obj.fn(...args)
        delete obj.fn
        return res
    }
    Function.prototype.myBind = function (obj,...args) {
        const fn = this
        return function (...args1) {
            const res = fn.apply(obj,args.concat(args1))
            return res
        }
    }
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值