前端面试题

1.深拷贝和浅拷贝的区别

(1)深拷贝:深拷贝则是创建一个新对象,新对象的内容是原始对象的完全复制。深拷贝不仅复制对象的引用,还复制了对象本身的数据。无论原始对象是否发生改变,深拷贝的对象都不会受到影响。

具体区别可以通过以下示例理解:

假设有一个原始对象 obj,其中包含一个列表属性 list_val。

深拷贝示例:

let obj1 = {name: '小明'}; 
let obj2 = JSON.parse(JSON.stringify(obj1));
obj2.name = '小红';
console.log(obj1.name); // 小明

(2)浅拷贝:浅拷贝是指创建一个新对象,新对象的内容是原始对象的引用。换句话说,浅拷贝只复制对象的引用,并没有复制对象本身的数据。因此,当原始对象发生改变时,浅拷贝的对象也会随之改变。

浅拷贝示例:

var person1={name:"张三",hobby:['篮球','足球','羽毛球']};
person2 = person1; // 浅拷贝
person2.name = 'hello'
console.log(person1);
console.log(person2);

从示例中可以看出,浅拷贝创建了一个新对象,但其列表属性仍然是原始对象的引用,修改原始对象会影响到浅拷贝的对象。而深拷贝则创建了一个完全独立的对象,不受原始对象改变的影响。

需要注意的是,深拷贝会比浅拷贝消耗更多的系统资源,因为它需要复制整个对象的数据。在某些情况下,选择使用浅拷贝或深拷贝取决于具体的需求和性能考虑。

2.什么是递归函数

1.递归函数指一个函数在它的函数体内调用它本身,执行递归函数将反复调用其自入新的一层,递归函数必须有结束条件。

当函数在一直递堆,直到遇到墙厚返回,这个墙就是结束条件。

所以递归要有两个要素,结束条件和递堆关系。

递归有两个基本要素:

(1)边界条件:确定递归到何时终止,也成为递归出口。

(2)递归模式:大问题是如何分解为小问题的,也称为递归体。递归函数只有具备了这两个要素,才能在有限次计算后得出结果

在递归函数中,调用函数和被调用函数是同一个函数,需要注意的是递归函数的调用层次,如果把调用递归函数的主函数称为第0层,进入函数后,首次递归调用自身称为第1层调用;从第i层递归调用自身称为第i+1层。反之,退出第i+1层调用应该返回第i层。

一个递归函数的调用过程类似于多个函数的嵌套的调用,只不过调用函数和被调用函数是同一个函数。为了保证递归函数的正确执行,系统需设立一个工作栈。具体地说,递归调用的内部执行过程如下:

(1)运动开始时,首先为递归调用建立一个工作栈,其结构包括值参、局部变量和返回地址;

(2)每次执行递归调用之前,把递归函数的值参和局部变量的当前值以及调用后的返回地址压栈;

(3)每次递归调用结束后,将栈顶元

扩展资料:

递归就是某个函数直接或间接地调用了自身,这种调用方式叫做递归调用。说白了,还是函数调用。既然是函数调用,那么就有一个雷打不动的原则:所有被调用的函数都将创建一个副本,各自为调用者服务,而不受其他函数的影响。

你的ff函数,递归多少次,就有多少个副本,再利用内存的栈式管理,反向退出。这个最好找一下“栈”这方面的东西看看,挺容易的,就像子弹匣一样,先进后出。

从某种意义上说,这是不对的,因为就像刚才说的,一旦被调用,他将在内存中复制出一份代码,再被调用就再复制一份,换句话说,你可以吧同一个函数的多次调用理解称谓多个不同函数的一次调用,这样也会会简单些。

再说=1和=0是为什么退出。递归,很需要注意的就是死递归,也就是说,某一个函数进入了无限调用自身的情况,永无止境地消耗内存等资源,这在编程方面是一大忌。

但凡是递归的函数,一定会在某一个地方存在能够返回上一层函数的代码,否则必定死递归。ff函数中,那个else就是返回的出口,你可以这样想,如果没有那个if来进行判断,你递归到什么时候算完?ff是不是会一直调用自己。

因为一旦某个函数A中调用了函数B(或者自己),那么A中的代码会停在调用的位置,而转向B中去执行,同理,如果B又调用函数C,那么B又停在调用的位置,去执行C,如果无限调用,那么程序是永远不会结束的。

3.js的this指向问题

1.箭头函数(⇒)

箭头函数this的指向不会发生改变,也就是说在创建箭头函数时就已经确定了;它的指向永远指向箭头函数外层的this。

        function fn1() {
            console.log(this);
 
            let fn2 = () => {
                console.log(this);
            }
            fn2(); //this->window
        }
        fn1();//this->window
 
//因为fn1函数中this的指向是window,所以fn2箭头函数this指向fn1函数也就是间接指向window
 

2.直接调用

在函数被直接调用时,this 将指向全局对象。在浏览器环境中全局对象是 Window,在 Node.js 环境中是 Global。

全局作用域中:this永远指向window

函数作用域中:

  • 如果函数直接被调用 this指向window 函数名()
  • 被对象的对象.属性()调用  函数中的this指向这个对象
 
        var obj = {
 
            fn: function fn1() {
                console.log(this);
            },
        }
        obj.fn(); //this->obj

3.对象.属性()调用

这里说的this指向就是上文中代码中的例子,有兴趣可以自行多尝试一下此场景中的this

4.new

当使用 new 关键字调用函数时,函数中的 this 一定是 JS 创建的新对象。

fun = () => {}
new fun() // throw error

从控制台中可以看出,箭头函数不能当做构造函数,所以不能与 new 一起执行。

5.bind

bind 是指 Function.prototype.bind()->原型里的方法

多次 bind 时只认第一次 bind 的值

例子:

function fn() {
  console.log(this)
}
 
fn.bind(1).bind(2)() // 1

**注意:**箭头函数中 this 不会被修改

fun = () => {
  // 这里 this 指向取决于外层 this
  console.log(this)
}
 
fun.bind(1)() // Window

bind 与 new

**注意:**new优先

function func() {
  console.log(this, this.__proto__ === func.prototype)
}
 
boundFunc = func.bind(1)
new boundFunc() // Object true

bind 函数中 this 不会被修改

function func() {
  console.log(this)
}
 
boundFunc = func.bind(1)
boundFunc.apply(2) // 1 bind优先

总结:将参数一一进行传递,在被函数调用时不会立马执行函数,返回有一个新函数,新函数中的this指向改变,不影响原来的函数的this指向

6.apply 和 call

作用:改变this指向

**区别:**调用时传递参数形式不同

  • **call:**将参数一一进行传递(改变函数中的this指向 指向第一个参数;并且执行函数)
  • **apply:**将参数用数组的形式传递(改变函数中的this指向 指向第一个参数;并且执行函数)

使用:

function Person(name,age){
  this.name = name;
  this.age = age;
}
var person = new Person("xiaoming",100);
var obj = {}
Person.apply(obj,['xiaoming',300]);//Person {name: 'xiaoming', age: 100};
Person.call(obj,'xiaozhang',300);//Person {name: 'xiaoming', age: 100};
console.log(person)

箭头函数中 this 不会被修改

func = () => {
  // 这里 this 指向取决于外层 this
  console.log(this)
}
 
func.apply(1) // Window

4.call,apply,bind的区别

1. 三者区别

  • call、bind、apply 都是 JavaScript 中用于改变函数执行上下文(即 this 指向)的方法。

  • call 和 apply 的作用是一样的,都是用来调用函数并且改变函数内部的 this 指向。区别在于传参的方式不同,call 的参数是一个一个传递的,而 apply 的参数是以数组的形式传递的。

  • bind 方法不会立即执行函数,而是返回一个新的函数,这个新的函数的 this 值被绑定到了指定的对象,调用时也可以传入参数。同时使用 bind 方法可以实现柯里化,即将函数转化为接收部分参数的函数。

callapplybind
是函数的标准调用也是函数的标准调用可以改变函数this指向,先改变函数this指向,然后再去调用函数,才可以执行
可以强制改变this指向也可以将this改变任意一个东西改变this指向的特点是不会立刻执行
可以将this改变成任意的一个东西传递参数的时候必须是一个数组使用与改变事件中的this指向 为什么?它不会立刻执行,可以让用户触发以后在执行
可以改变事件中的this指向 事件就直接执行了也可以改变方法中的this指向
还可以改变方法中的this指向不可以改变箭头函数的this指向

call

  • fn.call 当前实例(函数fn)通过原型链的查找机制,找到function.prototype上的call方法,function call(){[native code]}

  • fn.call() 把找到的call方法执行 当call方法执行的时候,内部处理了一些事情 1.首先把要操作的函数中的this关键字变为call方法第一个传递的实参 2.把call方法第二个及之后的实参获取到 3.把要操作的函数执行,并且把第二个以后传递进来的实参传递给函数

  • call中的细节

    1.非严格模式

    如果不传参数,或者第一个参数是null或nudefined,this都指向window

  let fn = function(a,b){
        console.log(this,a,b);
    }
    let obj = {name:"obj"};
    fn.call(obj,1,2);    // this:obj    a:1         b:2
    fn.call(1,2);        // this:1      a:2         b:undefined
    fn.call();           // this:window a:undefined b:undefined
    fn.call(null);       // this=window a=undefined b=undefined
    fn.call(undefined);  // this=window a=undefined b=undefined

2.严格模式第一个参数是谁,this就指向谁,包括null和undefined,如果不传参数this就是undefine

apply

  • apply:和call基本上一致,唯一区别在于传参方式

    apply把需要传递给fn的参数放到一个数组(或者类数组)中传递进去,虽然写的是一个数组,但是也相当于给fn一个个的传递

bind

  • bind:语法和call一模一样,区别在于立即执行还是等待执行,bind不兼容IE6~8

this改变为obj了,但是绑定的时候立即执行,当触发点击事件的时候执行的是fn的返回值undefined

bind会把fn中的this预处理为obj,此时fn没有执行,当点击的时候才会把fn执行

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值