美团-前端开发面经(五)

扶摇就业

共 5396字,需浏览 11分钟

 ·

2021-04-12 23:47

点击上方蓝字关注我们




箭头函数和普通函数的区别





一.外形不同:箭头函数使用箭头定义,普通函数中没有

代码实例如下:

// 普通函数function func(){  // code}// 箭头函数let func=()=>{  // code}


二.箭头函数都是匿名函数

普通函数可以有匿名函数,也可以有具体名函数,但是箭头函数都是匿名函数。

代码实例如下:

// 具名函数function func(){  // code} // 匿名函数let func=function(){  // code}

// 箭头函数全都是匿名函数let func=()=>{ // code}


三.箭头函数不能用于构造函数,不能使用new

普通函数可以用于构造函数,以此创建对象实例。

代码实例如下:

function Person(name,age){   this.name=name;   this.age=age;}let admin=new Person("恩诺小弦",18);console.log(admin.name);console.log(admin.age);


代码运行如下:


恩诺小弦

18


Person用作构造函数,通过它可以创建实例化对象。

但是构造函数不能用作构造函数。


四.箭头函数中this的指向不同

在普通函数中,this总是指向调用它的对象,如果用作构造函数,this指向创建的对象实例。

1.箭头函数本身不创建this

也可以说箭头函数本身没有this,但是它在声明时可以捕获其所在上下文的this供自己使用。

注意:this一旦被捕获,就不再发生变化

var webName="捕获成功";let func=()=>{  console.log(this.webName);}func();

运行结果如下:


捕获成功


代码分析:箭头函数在全局作用域声明,所以它捕获全局作用域中的this,this指向window对象。


例子:

var name = "恩诺1";function wrap(){  this.name="恩诺2";  let func=() => {    console.log(this.name);  }  func();}let en=new wrap();

运行结果如下:


恩诺2


代码分析:

(1)wrap()用作构造函数。

(2)使用new调用wrap()函数之后,此函数作用域中的this指向创建的实例化对象。

(3)箭头函数此时被声明,捕获这个this。

(4)所以打印的是恩诺2,而不是恩诺1。

2.结合call(),apply()方法使用

箭头函数结合call(),apply()方法调用一个函数时,只传入一个参数对this没有影响。

let obj2 = {    a: 10,    b: function(n) {        let f = (n) => n + this.a;        return f(n);    },    c: function(n) {        let f = (n) => n + this.a;        let m = {            a: 20        };        return f.call(m,n);    }};console.log(obj2.b(1));  // 结果:11console.log(obj2.c(1)); // 结果:11


3.箭头函数不绑定arguments,取而代之用rest参数…解决

每一个普通函数调用后都具有一个arguments对象,用来存储实际传递的参数。但是箭头函数并没有此对象。

function A(a){  console.log(arguments);}A(1,2,3,4,5,8);  //  [1, 2, 3, 4, 5, 8, callee: ƒ, Symbol(Symbol.iterator): ƒ]



let B = (b)=>{ console.log(arguments);}B(2,92,32,32); // Uncaught ReferenceError: arguments is not defined



let C = (...c) => { console.log(c);}C(3,82,32,11323); // [3, 82, 32, 11323]


4.其他区别

(1).箭头函数不能Generator函数,不能使用yeild关键字。

(2).箭头函数不具有prototype原型对象。

(3).箭头函数不具有super。

(4).箭头函数不具有new.target。


总结:

(1).箭头函数的 this 永远指向其上下文的 this ,任何方法都改变不了其指向,如 call() , bind() , apply()

(2).普通函数的this指向调用它的那个对象







深拷贝怎么实现





深拷贝和浅拷贝的概念:

深拷贝拷贝的是对象或者数组内部数据的实体,重新开辟了内存空间存储数据;

浅拷贝拷贝的是引用类型的指针,副本和原数组或对象指向同一个内存;


另一个前提:实现浅复制和深复制的方法区分要看该方法是否对存在于数组或对象的深层的引用类型的值进行真实值的复制,而不是仅对引用进行拷贝,如果这样理解,那么下面的标题的定义应该要做一些修改。

一个方法是是实现了深拷贝还是浅拷贝,要看它是否在拷贝对象或数组时,出现拷贝指针的情况,出现了就是实现了浅拷贝,否则就是实现了深拷贝;


一些建立在数组中的各项或对象中的属性值均为基本类型值的前提下的深拷贝的例子:

数组

1.concat方法

  

var arr1 = [1, 2, 3, 4]    var arr2 = arr1.concat()//复制当前数组并返回实现深拷贝的副本,arr1独立而不受影响    console.log(arr2);[1, 2, 3, 4]

var arr3 = arr1.concat([5, 6, 7])//将数组中的每一项都添加到深拷贝的副本数组中 console.log(arr3);//[1, 2, 3, 4, 5, 6, 7]


2.slice方法

var arr = [1, 2, 3, 4]

var arr1 = arr.slice(1)//接收1到2个参数,一个参数时,返回参数指定调用方法数组位置到末尾的值组成的深拷贝副本 console.log(arr1);//[2, 3, 4]

var arr2 = arr.slice(0, 1) console.log(arr2);//[1]


3.扩展运算符

扩展运算符将一个数组转为用逗号分隔的参数序列

const arr = [1, 2, 3]

const arr1 = [...arr] arr1.push(4) console.log(arr1);//[1, 2, 3, 4] console.log(arr);//[1, 2, 3]注意,对于数组项是引用类型的数据,就无法实现深拷贝了。const arr = [1, 2, 3, [4, 5, 6]] const arr1 = [...arr] console.log(arr1); arr1[3].push(7) console.log(arr1); console.log(arr);//[1, 2, 3, [4, 5, 6, 7]]更改副本,原数组也被改变



对象

先看一下浅拷贝的例子

var obj = {      a: 1,      b: 2    }

var obj1 = {}

obj1 = obj//浅拷贝

console.log(obj1);//{a: 1, b: 2} obj1.c = 3 console.log(obj);//{a: 1, b: 2, c: 3},更改obj1,obj也被改变


1.Object.assign(目标对象, 源对象),源对象的所有可枚举属性都复制到目标对象上

var obj = {      a: 1,      b: 2    }

var obj1 = {}

Object.assign(obj1, obj) console.log(obj1);//{a: 1, b: 2} obj1.c = 3 console.log(obj1);//{a: 1, b: 2, c: 3} console.log(obj);//{a: 1, b: 2},obj没有被改变,实现了深拷贝


2.JSON

var obj = {      a: 1,      b: 2    }        // 将javascript值转为JSON字符串    var jsonText = JSON.stringify(obj)    console.log(jsonText);//{"a":1,"b":2}    // 把JSON字符串转为javascript值    var obj1 = JSON.parse(jsonText)    console.log(obj1);//{a: 1, b: 2}

// 修改obj1,看原对象值是否被改变 obj1.c = 3 console.log(obj1);//{a: 1, b: 2, c: 3} console.log(obj);//{a: 1, b: 2},没被改变,实现了深拷贝


3.扩展运算符

 

var obj = {        a: 1,        b: 2      }

var obj1 = {...obj} console.log(obj);//{a: 1, b: 2} console.log(obj1);//{a: 1, b: 2} obj.a = 33 console.log(obj);//{a: 33, b: 2} console.log(obj1);//{a: 1, b: 2}没改变,实现了深拷贝


二、完全实现深拷贝的方法

1.JSON方法

var obj = {  a: {    c: 2,    d: [9, 8, 7]  },  b: 4}var jsontext = JSON.stringify(obj)var obj1 =JSON.parse(jsontext) console.log(obj);console.log(obj1);

obj.a.d[0] = 666

console.log(obj);console.log(obj1);


注意,此处代码放在node环境中运行才会得到期望的结果;

原因为:

并没有什么规范或一组需求指定console.* 方法族如何工作——它们并不是JavaScript 正式的一部分,而是由宿主环境(请参考本书的“类型和语法”部分)添加到JavaScript 中的。因此,不同的浏览器和JavaScript 环境可以按照自己的意愿来实现,有时候这会引起混淆。

尤其要提出的是,在某些条件下,某些浏览器的console.log(..) 并不会把传入的内容立即输出。出现这种情况的主要原因是,在许多程序(不只是JavaScript)中,I/O 是非常低速的阻塞部分。所以,(从页面/UI 的角度来说)浏览器在后台异步处理控制台I/O 能够提高性能,这时用户甚至可能根本意识不到其发生。


2.函数库lodash的_.cloneDeep方法

var _ = require('lodash')

var obj = { a: { c: 2, d: [9, 8, 7] }, b: 4}

var obj1 = _.cloneDeep(obj)

console.log(obj === obj1);//false


3.递归实现深拷贝

function copy(object) {  // 判断传入的参数是数组还是对象  let target = object instanceof Array ? [] : {}  for (const [k ,v] of Object.entries(object)) {    target[k] = typeof v == 'object' ? copy(v) : v  }  return target}

var obj1 = copy(obj)console.log(obj.a.d === obj1.a.d);//false










往期精彩




百度-前端开发面经(一)
网易有道-前端开发面经(一)
阿里-前端开发面经(一)


 扫码二维码

获取更多面经

扶遥就业


浏览 32
点赞
评论
收藏
分享

手机扫一扫分享

分享
举报
评论
图片
表情
推荐
点赞
评论
收藏
分享

手机扫一扫分享

分享
举报