前端面试知识点整理——JavaScript

前端面试知识点整理——JavaScript

一、js基本数据类型和复杂数据类型的区别

基本数据类型(简单数据类型):number、boolean、null、undefined、string、symbol、(bigInt)
复杂数据类型(引用数据类型):object(Object、Array、Function、Date)

区别:
1.声明变量时不同的内存分配
2.不同的访问机制
3.复制变量时的不同
4.参数传递时的不同
在这里插入图片描述
基本包装类型:String、Boolean、Number都是,是一种把简单数据类型包装成复杂数据类型的过程,让简单数据类型拥有了属性和方法。如str.length

二、var、let、const的区别

1.let、const都是块级局部变量,只在当前代码块起作用,var不受块级作用域的限制,他只存在全局作用域和函数作用域的概念
2.let、const必须先声明后使用,而且const必须在声明时进行初始化,后面不能进行修改(对象或数组本身不能修改,但是里面的内容可以修改)。而var存在变量提升,可以先使用后声明。
3.同一作用域下,let、const不能声明同名变量,但是var可以

三、ES5 继承和 ES6 继承的区别

ES5:ES5的继承是通过构造函数(可以定义私有属性/方法、不能定义共享的属性/方法)和原型对象(可以定义公有属性和方法、不能定义私有属性方法)来实现的,称为组合继承。在这里,子构造函数属性的继承可以通过Father.call(this,参数1,参数2)来实现(子类可以拥有自己独立的属性),改变了父构造函数的this指向;方法的继承是通过让子类原型对象 等于父类实例,并通过原型链的查找机制来获得位于 父原型对象中的方法(方法可复用)。这里不能通过 子原型对象 = 父原型对象的方式,这会导致两个对象相互影响,同步改变。
ES6:ES6新增了class的概念,子类通过extends来继承父类,并利用super()方法来调用父类的构造函数和方法。
1、extends关键字:子类通过extends关键字,继承了父类的所有属性和方法。父类中的所有方法默认是添加到子类的原型上,所以extends继承的实质仍然是原型链。
2、super关键字:super这个关键字,既可以当作函数使用,也可以当作对象使用。当作函数使用时,super代表父类的构造函数,并在子类中隐式执行Parent.apply(this),从而将父类实例对象的属性和方法,添加到子类的this上面。super作为对象时,在子类中指向父类的原型对象。
在这里插入图片描述

四、null 和 undefined 区别

null和undefined都在if语句中都是false, ==返回true
null表示"没有对象",即该处不应该有值。typeof null 返回的是object,null+1=1
undefined表示"缺少值",就是此处应该有一个值,但是还没有定义。 typeof undefined返回undefined , undefined + 1=NaN
典型用法:
(1)变量被声明了,但没有赋值时,就等于undefined。
(2) 调用函数时,应该提供的参数没有提供,该参数等于undefined。
(3)对象没有赋值的属性,该属性的值为undefined。
(4)函数没有返回值时,默认返回undefined。

五、typeof和 instanceof 区别

typeof适用于基础数据类型判断,引用类型判断都是object(除了function),可以返回number、string、boolean、function、object、undefined、symbol,注意null返回object。 instanceof 判断一个实例是否属于某种类型,返回一个布尔值,但严重存在原型继承

六、instanceof 的原理和弊端

/**
 * instanceof的原理
 * 就是左侧的参数实例的__proto__属性 沿着原型链一直往上延伸查找每一个原型 是否与右侧的参数的原型相等
 */
function instance_of(L, R) {
    let O = R.prototype
    L = L.__proto__
    while(L) {
        if(L === null) {
            return false
        }
        if(L === O) {
            return true
        }
        L = L.__proto__
    }
    return false
}
console.log(instance_of([], Array))
console.log(instance_of([], Object))

解决原型链的问题:
Array.isArray()针对数组的操作
Object.prototype.toString.call(’’) === ‘[object String]’

解决方案
因为js中的一切都是对象,任何都不例外,对所有值类型应用 Object.prototype.toString.call() 方法结果如下:

console.log(Object.prototype.toString.call(123)) //[object Number]
console.log(Object.prototype.toString.call(‘123’)) //[object String]
console.log(Object.prototype.toString.call(undefined)) //[object Undefined]
console.log(Object.prototype.toString.call(true)) //[object Boolean]
console.log(Object.prototype.toString.call({})) //[object Object]
console.log(Object.prototype.toString.call([])) //[object Array]
console.log(Object.prototype.toString.call(function(){})) //[object Function]

判断是否为函数

function isFunction(it) {
return Object.prototype.toString.call(it) === ‘[object Function]’;
}

判断是否为数组:

function isArray(o) {
return Object.prototype.toString.call(o) === ‘[object Array]’;
}

七、null > 0, null < 0, null >= 0, null <= 0, null == 0 分别输出什么?

1.在关系运算符中,null,undefined会被Number()强制转换成数字类型;
Number(null) //0
Number(undefined) //NaN
因此:
null >= 0; //true
null <= 0; //true
null > 0; //false
null < 0; //false
2.在相等运算符中,null,undefined则不会转化为数字类型,而是经过特殊处理
null == 0 //false
null == undefined //true
null == null //true
undefined == undefined //true

八、js 的类型转换

1.隐式转换
2.转为String:String()、toString()
3.转为数值:Number()、parseInt()、parseFloat()
4.转换成布尔值:Boolean()

九、prototype 是啥,什么是原型,什么是实例原型,说说你的理解

每个构造函数都有一个prototype属性,指向一个对象,这个prototype就是一个原型对象,他所拥有的所有属性和方法,都会被构造函数所拥有
每个对象(包括实例)都有一个__proto__属性,指向构造函数的prototype原型对象,它提供了一种查找机制(原型链查找机制)

十、js 为什么不支持多继承

javascript是可以利用call方法和prototype属性来实现多继承的。继承方法与单继承相似,只是将需要继承的多个父类依次实现,另外对于属性或共有方法重命的时候,以最后继承的属性和方法为主。因为会覆盖前面的继承。

十一、深拷贝和浅拷贝的区别

【JS】深拷贝与浅拷贝的区别,实现深拷贝的几种方法
手写深拷贝函数:

function deepCopy(obj) {
    var cloneObj;
    if (obj && typeof obj !== 'object') cloneObj = obj;
    else if (obj && typeof obj === 'object') {
        cloneObj = Array.isArray(obj) ? [] : {};
        for (let k in obj) {
            if (obj.hasOwnProperty(k)) {
                if (obj[k] && typeof obj[k] === 'object') {
                    cloneObj[k] = deepCopy(obj[k]); //递归
                }
                else {
                    cloneObj[k] = obj[k];
                }
            }
        }
    }
    return cloneObj;
}

var obj = {
    name: 'vivian',
    age: 18,
    course: {
        time: 12,
        msg: 'nihao'
    }
}

var o = deepCopy(obj);
console.log(o);

十二、匿名函数、箭头函数

匿名函数:就是定义时未直接指定名称的函数
因为非匿名函数在定义时就已经创建了函数对象和作用域对象,因此,即使未调用,也占用内存空间
而匿名函数仅在调用时,才临时创建函数对象和作用域链对象,调用完立即释放,所以匿名函数比非匿名函数更节省内存空间

箭头函数:
(1)箭头函数没有自己的this,导致内部的this就是外层代码的this,他的this是定义时候的对象,而不是使用时所在的对象。call、bind、apply都不能改变
(2)因为箭头函数没有this,因此他不能当作构造函数,也就是说不能使用new命令,否则会抛出一个错误
(3)不可以使用arguments对象,该对象在函数体内不存在
(4)不可以使用yield命令,因此箭头函数不能用作generator函数

十三、变量提升

预解析+执行

好处
提高性能:预先为变量分配栈空间
容错性更好:使一些不规范的代码也可以正常执行

问题:函数内一些变量提升会导致输出undefined、循环变量输出最后的值

十四、NaN

typeof NaN;返回“number”
NaN != NaN;返回true

十五、map和object的区别

在这里插入图片描述
map有size、set、get、has、delete、clear方法
map结构提供三个遍历器生成函数和一个遍历方法:keys() values() entries() forEach()

十六、map和weakmap区别

weakmap有set、get、has、delete方法,没有size和clear
区别:weakmap只接受对象作为键名,weakmap的键名所引用的对象都是弱引用,也就是不计引用,因此,周期时间内,会被垃圾回收器回收,不易造成内存泄漏

十七、常用的正则表达式

//以字母开头 后面可以是数字、字母、下划线 长度为6-30
var reg = /^[a-zA-Z]\w{5,29}$/g;

//手机号码
var reg = /^1[34578]\d{9}$/g;

//匹配日期 yyyy-mm-dd
var reg = /^[0-9]{4}-(0[1-9]|1[1-2])-(0[1-9]|[12][1-9]|3[01])$/g;

//匹配qq号
var reg = /^[1-9][0-9]{4,10}$/g;

十八、json

JSON.stringify() 传入一个json格式的数据结构,将其转换为一个json字符串
JSON.parse() 将json字符串转换为js数据结构

十九、将类数组转为数组

Array.prototype.slice.call(arguments);
Array.prototype.splice.call(arguments, 0);
Array.prototype.concat.apply([], arguments);
Array.from(arguments);

例如:要想arguments使用数组的forEach方法:

//法一
function foo() {
	Array.prototype.forEach.call(arguments, a => console.log(a));
}
//法二
function foo() {
	let arr = Array.from(arguments);
	arr.forEach(a => console.log(a));
}
//法三
function foo() {
	let arr = [...arguments];
	arr.forEach(a => console.log(a));
}

二十、常见位运算符

&(与)|(或)^(异或)~(非)<<(左移) >>(右移)

二十一、ajax请求

let xhr = new XMLHttpRequest();
xhr.open('GET', '/url', true);
xhr.send();
xhr.onreadystatechange = function () {
    if (xhr.readyState == 4 && xhr.status == 200) {
        alert(xhr.responseText);
    } else {
        alert(xhr.statusText);
    }
}

//请求成功回调函数
xhr.onload = e => {
    console.log('request success');
}
//请求结束
xhr.onloadend = e => {
    console.log('request loadend');
}
//请求出错
xhr.onerror = e => {
    console.log('request error');
}
//请求超时
xhr.ontimeout = e => {
    console.log('request timeout');
}


//设置超时时间 0表示永不超时
xhr.timeout = 0;
//初始化请求
xhr.open('GET/POST/DELETE/...', 'URL', true || false);
//设置期望返回的数据类型'json' 'text' 'document'...
xhr.responseType = '';
//设置请求头
xhr.setRequestHeader('', '');
//发动请求
xhr.send(null || new FormData || 'a=1&b=2' || 'json字符串');

用promise封装一下:

function getJSON(url) {
	let promise = new Promise(function(resolve, reject) {
		let xhr = new XMLHttpRequest();
		xhr.open("GET", url, true);
		xhr.onreadystatechange = function() {
			if(this.readyState !== 4) return;
			if(this.status === 200) {
				resolve(response);
			} else {
				reject(new Error(this.statusText));
			}
		};
		//设置错误监听函数
		xhr.onerror = function() {
			reject(new Error(this.statusText));
		};
		//设置响应的数据类型
		xhr.responseType = "json";
		//设置请求头信息
		xhr.setRequestHeader("Accept", "application/json");
		//发送http请求
		xhr.send(null);
	});
	return promise;
}

二十二、异步编程的实现方式

(1)回调函数:多个回调函数会造成回调地狱,代码不易维护
(2)Promise:以同步的方式编写异步操作,使用then的链式调用,避免了回调地狱
(3)generator:调用generator函数会返回一个遍历器对象,代表generator的内部指针,遇到yield停止执行,next()恢复执行,返回一个有着value和done两个属性的对象,value表示当前内部状态的值(yield表达式后面的表达式的值),done是布尔值,表示是否遍历结束。next()会执行到遇到下一个yield为止。
yield表达式本身返回undefined,可以用过next(’’)的参数给上一个yield作为返回值。可以在generator运行的不同阶段,从外部向内部注入不同的值,从而调整函数行为。
yield* 返回一份遍历器对象,任何数据结构只要有iterator接口,就可以被yield*遍历
(4)async:async函数是generator和promise实现的一个自动执行的语法糖,它内部自带执行器,当函数内部执行到一个await语句的时候,如果语句返回一个promise对象,函数就会等待promise对象的状态变为resolve之后再继续执行。因此可以将异步逻辑,转化为同步的顺序来书写,并且这个函数可以自动执行。

async/await对比Promise的优势

  • 代码读起来更加同步,Promise虽然摆脱了回调地狱,但是then的链式调⽤也会带来额外的阅读负担
  • Promise传递中间值⾮常麻烦,⽽async/await⼏乎是同步的写法,⾮常优雅
  • 错误处理友好,async/await可以⽤成熟的try/catch,Promise的错误捕获⾮常冗余
  • 调试友好,Promise的调试很差,由于没有代码块,你不能在⼀个返回表达式的箭头函数中设置断点,如果你在⼀个.then代码块中使⽤调试器的步进(step-over)功能,调试器并不会进⼊后续的.then代码块,因为调试器只能跟踪同步代码的每⼀步。

参考:
「2021」高频前端面试题汇总之JavaScript篇(上)
「2021」高频前端面试题汇总之JavaScript篇(下) 这里边有些输出题挺不错的
Generator 函数的语法

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值