一线大厂前端经典面试题JavaScript部分

本文汇集了BAT等一线大厂的前端面试题,深入探讨JavaScript的堆栈内存、闭包作用域、对象(数组)深浅克隆以及EventLoop机制,助你全面掌握JavaScript核心技术。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

BAT笔试题中几道关于堆栈内存和闭包作用域的问题

let a = {}, b = '0', c = 0;
a[b] = 'JavaScript';
a[c] = 'HTML+CSS';
console.log(a[b]); //HTML+CSS

//原因:对象中属性名不能重复,一般都为字符串属性,字符串属性名跟数字属性名是一样的
let a  = {}, b = Symbol('1'), c = Symbol('1');
a[b] = 'JavaScript';
a[c] = 'HTML+CSS';
console.log(a[b]);//JavaScript

//Symbol的特点是:创建的唯一值。Symbol('1')与Symbol('1')是不相等的
let a = {},
b = {
    n: '1'
},
c = {
    m: '2'
}

a[b] = 'JavaScript';
a[c] = 'HTML+CSS';
console.log(a[b]);//HTML+CSS

//对象做属性名时会通过.toString方法被转换为字符串,而对象转换为字符串后都是"[object, Object]"
// 所以b和c转换为字符串后是一样的。

 

 var test = (function(i){
    	return function(){
        	alert(i*=2);// alert输出结果都会转为字符串
        }
    })(2)
    
    test(5); // '4'
//首先是创建一个自调用函数,在函数中又返回一个函数,这里有闭包产生。
//立即执行函数接收的实参是2,然后返回一个函数。
//这时test是一个无参函数,函数体就是alert(i*=2),由于闭包作用这里的i是立即执行函数的参数i,而立即执行函数执行时传入的参数是2
//当调用test函数时虽然传了个参数5,但由于test是一个无参函数,因此5没有任何作用
//所以最终结果是4,而又因alert会把内容转换为字符串,所以最终alert的是字符串4
var a = 0, b = 0;

function A(a){
    A = function(b){
        alert(a+b++);
    }
    alert(a++)
}

A(1); // "1"
A(2); // "4"

对象(数组)的深克隆和浅克隆(头条)

let obj = {
    a: 100,
    b: [10, 20, 30],
    c: {
        x: 10
    },
    d: /^\d+S/
};

let arr = [10, [100, 200], {
    x: 10,
    y: 20
}];

//=>浅克隆

//es6
let obj2 = {...obj};
//es5
let obj2 = {}
for(let key in obj){
    if(obj.hasOwnProperty(key)){
        obj2[key] = obj[key];
    }
}

//=>深克隆

//let obj2 = JSON.parse(JSON.stringify(obj));//先转成json字符串再转成对象,但是如果对象内部有函数,正则或日期则转换时会有问题

function deepClone(obj){
    if(obj === null) return null;
    if(typeof obj !== "object") return obj;
    if(obj instanceof RegExp) return new RegExp(obj);
    if(obj instanceof Date) return new Date(obj);
    let cloneObj = new obj.constructor;
    for(let key in obj){
        if(obj.hasOwnProperty(key)){
            cloneObj[key] = deepClone(obj[key]);
        }
    }
    return cloneObj;
}

let obj2 = deepClone(obj);

一道关于面向对象面试题引发的血案(阿里)

function Foo(){
    getName = function(){
        console.log(1);
    };
    return this;
}

Foo.getName = function(){
    console.log(2);
}

Foo.prototype.getName = function(){
    console.log(3);
}

var getName = function(){
    console.log(4);
};

function getName(){
    console.log(5);
}

Foo.getName();//2
getName();//4
Foo().getName();//1
getName();//1
new Foo.getName();//2
new Foo().getName();//3
new new Foo().getName();//3

一道面试题让你彻底掌握JS中的EventLoop(头条)

async function async1(){
    console.log('async1 start');
    await async2();
    console.log('async1 end');
}

async function async2(){
    console.log('async2');
}

console.log('script start');

setTimeout(function(){
    console.log('setTimeout');
},0);

async1();

new Promise(function(resolve){
    console.log('promise1');
    resolve();
}).then(function(){
    console.log('promise2');
});

console.log('script end');

function A(){
    alert(1);
}

function Func(){
    A = function(){
        alert(2);
    }
    return this;
}

Func.A = A;//相当于 Func.A = function(){alert(1)}
Func.prototype = {
    A: () => {
        alert(3);
    }
}

A();//1
Func.A();//1
Func().A();//2 先调用Func函数,重写全局函数A并返回this(window),然后执行window.A(),此时A已经被重写,所以输出2
new Func.A();//1
new Func().A();//3 相当于实例.A(),所以去原型里找,输出3
new new Func().A();//报错,原因是原型中的A是箭头函数,不支持new
var x = 2;
var y = {
    x: 3,
    z: (function(x){
        this.x *= x;
        x += 2;
        return function(n){
            this.x *= n;
            x += 3;
            console.log(x);
        }
    })(x)
};

var m = y.z;
//y.z是自调用闭包函数,z函数自调用时传出的参数x值为全局x的值2,在函数内部执行x+=2后x变为4,然后返回一个函数。当调用m(4)时,实际上调用的是z返回的函数,此时n为4,x也为4,在执行x += 3后变为7所以输出7
m(4);//7

//当执行y.z(5)时,n的值为5,this.x *= n中this.x指的是y对象的x属性,值为3,执行*=n后变为15,
//而函数中的x来自上级作用域中的x值为7,在执行完 +=3后变为10所以紧跟着console.log输出10
y.z(5);//10

//在执行完m(4)时window.x变为16, 而y.x在执行y.z(5)后变为15
console.log(x, y.x);// 16, 15
var x = 0, y = 1;
function fn(){
    x += 2;
    fn = function(y){
        console.log(y + (--x));
    }
    console.log(x, y);
}

fn(3);//2,1  1.执行代码在fn中对x进行+=2操作,x变为2,2. fn重新指向另一个函数,3.输出x和y的值2,1

fn(4);//5 fn已经指向另一个函数即console.log(y + (--x));y为传入的参数4,x则为上级作用域中的x值为2,自减后变为1 所以输出4+1=5
console.log(x, y);//1,1 执行完上两个步骤后x变为1,全程未对全局变量y操作,所以y依然是1
setTimeout(() => {
    console.log(1);
}, 20);

console.log(2);

setTimeout(() => {
    console.log(3);
}, 10);

console.log(4);
console.time("AA");

for(let i = 0; i < 90000000; i++){
    //do somthing这里为了代码阻塞
}

console.timeEnd('AA');

console.log(5);

setTimeout(() => {
    console.log(6);
}, 8);

console.log(7);

setTimeout(() => {
    console.log(8);
}, 15);

console.log(9);

//结果输出:
// 2 4 时间长 5 7 9 3 1 6 8
//在代码从上到下执行时优先执行console.log/time/timeEnd,然后将setTimeout添加到宏任务列队中,在for循环阻塞前先加了两个setTimeout,阻塞后又加了2个。所以在主栈代码执行完成后开始执行列队中的任务,因为阻塞前的先添加进去的所以优先按顺序执行,然后再执行阻塞后的即后添加到列队中的。
// 当a为何值时,下面的条件能够成立
var a;

if(a == 1 && a == 2 && a == 3){
    console.log('条件成立');
}

//在==进行比较时,对象会调用自身的toString方法转换为字符串后再转换为数字进行比较,所以可以利用这一点对toString进行重写以达到目的

//1. 每比较一次都会调用一次toString,i就会加1,从而使条件成立
var a = {
    i: 0,
    toString: function(){
        return ++i;
    }
}

//2. 利用数组的shift和对象的toString
var a = [1, 2, 3];
a.toString = a.shift;

//3. 利用Object.defineProperty()来监听window对象的a属性并重写get方法
//这里不用a是因为在get中进行自加时又会调用get方法导致死循环
var i = 0;
Object.defineProperty(window, 'a', {
    get: function(){
        return ++i;
    }
});

 

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值