八股文-js篇
八股文-js篇
- 1. 延迟执行js的方式
- 2. js的数据类型
- 3. null 和 undefined的区别
- 4. == 和 ===的区别
- 5. js微任务和宏任务
- 6. js作用域
- 7. js对象
- 9. JS作用域+this指向+原型
- 8. js判断数组
- 9. slice作用、splice是否会改变原数组
- 10. js数组去重
- 11. 找出数组最大值
- 12. 给字符串新增方法实现功能
- 13. 找出字符串出现最多次数的字符以及次数
- 14. new操作符具体做了什么
- 15. 闭包
- 16. Promise 的三种状态是什么?如何转换?
- 17. Promise.all 和 Promise.race 的区别?
- 18. async/await 的作用是什么?
- 19. async/await 和 Promise 的关系?
- 20. instanceof 和 typeof
- 21. Map
- 22. 箭头函数和普通函数
1. 延迟执行js的方式
defer
(等html全部解析完成,才会执行js代码,按顺序执行)
defer属性只对外部脚本文件有效。这个属性表示脚本在执行的时候不会改变页面的结构,脚本会被延迟到整个页面都解析完毕后(即页面解析到</html>标签后)再运行。不过在实际当中,推迟执行的脚本不一定总会按顺序执行或者在DOMContenLoaded事件之前执行,因此 最好只包含一个这样的脚本。async
(不按顺序执行,谁先加载完谁先执行)
async属性只适用于外部脚本。给脚本添加async实行的目的是告诉浏览器,不必等该脚本下载和执行后再加载页面,同样也不必等该异步脚本下载和执行后再加载其他脚本。因此,异步脚本不应该再加载期间修改DOM。异步脚本保证会在页面的load事件前执行,但可能在DOMContentLoaded之前或之后。一般不推荐。
2. js的数据类型
- 简单数据类型(7种):
undefined
、null
、boolean
、number
、string
、symbol
、bigInt
- 复杂数据类型(1种):
object
console.log(true + 1) // 2
console.log(undefined + 1) // NaN
// 字符串和其他类型相加会有隐式转换
console.log('name' + true) // 'nametrue'
console.log('1' + 1) // '11'
console.log('1' + undefined ) // '1undefined '
console.log(typeof null) // object
3. null 和 undefined的区别
null
:空。undefined
:未定义。
先有null后有undefined。最初设计null时参考了java。null会被隐式转换为0,不容易被发现错误,为了填补这个问题设计出了undefined。
console.log(typeof null) // object
console.log(typeof undefined) // 'undefined'console.log(Number(undefined)) // 0
console.log(Number(undefined)) // NaN
4. == 和 ===的区别
==
:比较值,隐式转换值相等时会判true===
:比较值且比较类型
5. js微任务和宏任务
js是单线程语言,就是同一时间只能做一件事。js的主要用途是和用户互动,为了避免复杂性,单线程就成了js的核心特征。
js代码执行顺序:
- 同步的任务都执行完了才会执行事件循环(包含微任务和宏任务,先执行完微任务再执行宏任务)的内容。
- 先执行同步代码,遇到异步宏任务则将异步宏任务放入宏任务队列中,遇到异步微任务则将异步微任务放入微任务队列中,当所有同步代码执行完毕后,再将异步微任务从队列中调入主线程执行,微任务执行完毕后再将异步宏任务从队列中调入主线程执行,一直循环直至所有任务执行完毕。
宏任务和微任务:
- 宏任务:script、setTimeout、setInterval、postMessage
- 微任务:Promise.then ES6、Object.observer、MutationObserver、process.nextTick
- 先执行同步再执行异步,异步遇到微任务,先执行微任务,执行完后如果没有微任务,就执行下一个宏任务,如果有微任务,就按顺序一个一个执行微任务
6. js作用域
- 除了函数外,js没有块级作用域
- 作用域链:内部可以访问外部变量(就近原则),但外部 不能访问内部变量
- 注意声明变量是用var还是没有写(没有写默认window.)
- var变量声明提升机制
- 优先级:声明变量 > 普通函数(普通声明函数是不看函数的顺序的) > 参数 > 变量提升
function c(){var b = 1;function a(){console.log( b ); // undefinedvar b = 2;console.log( b ); // 2}a();console.log( b ); // 1
}
c();
var name = 'a';
(function(){if( typeof name == 'undefined' ){var name = 'b';console.log('111'+name);}else{console.log('222'+name);}
})()
// 111b
function fun( a ){console.log( a ); //ƒ a(){}var a = 10;function a(){}
}
fun( 100 );
7. js对象
- 对象是new出来的,对象不相等(跟存储的是指针有关)
- 对象注意:引用类型
- 对象的key都是字符串类型
- 对象查找某个属性或方法:原型链
console.log([1]==[1]) // false
let a = [1]
let b = a
console.log(a === b) // true
var obj1 = {a:'hellow'
}
var obj2 = obj1;
obj2.a = 'world';
console.log(obj1); //{a:world}
(function(){console.log(a); //undefinedvar a = 1;
})();
var a = {}
var b = {key:'a'
}
var c = {key:'c'
}a[b] = '123';
a[c] = '456';console.log( a[b] ); // 456
9. JS作用域+this指向+原型
function Foo(){getName = function(){console.log(1)} //注意是全局的window.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();//3
var o = {a:10,b:{a:2,fn:function(){console.log( this.a ); // 2console.log( this ); // {a: 2, fn: ƒ} 代表b对象}}
}
o.b.fn();
window.name = 'ByteDance';
function A(){this.name = 123;
}
A.prototype.getA = function(){console.log( this ); // windowreturn this.name + 1;
}
let a = new A();
let funcA = a.getA;
funcA(); // 'ByteDance1'
var length = 10;
function fn(){return this.length + 1;
}
var obj = {length:5,test1:function(){return fn();},test3:fn,
}
obj.test2 = fn;
console.log( obj.test1() ); // 11
console.log( obj.test3() ); // 6
console.log( fn()===obj.test2() ); // false
console.log( obj.test1() == obj.test2() ); // false
8. js判断数组
Array.isArray()
instanceof
[] instanceof ArraytoString.call()
原型判断 Object.prototype.toString.call(arr).indexOf(‘Array’) > -1isPrototypeOf()
Array.prototype.isPrototypeOf(arr)constructor
arr.constructor.toString().indexOf(‘Array’) > -1
9. slice作用、splice是否会改变原数组
slice()
用来截取数组,返回一个新数组splice()
用来插入、删除、替换原数组,返回被删除的元素;会改变原数组
10. js数组去重
- Set结构
new Set()
(集合天然去重)[…new Set(arr)] filter()
配合indexOf()
- 遍历配合
indexOf()
- 遍历配合
includes()
11. 找出数组最大值
Math.max(...[1,45,15464,2])
12. 给字符串新增方法实现功能
给字符串对象定义一个addPrefix函数,当传入一个字符串str时,它会返回新的带有指定前缀的字符串,例如:
console.log( ‘world’.addPrefix(‘hello’) ) 控制台会输出helloworld
String.prototype.addPrefix = function(str){return str + this;
}
console.log( 'world'.addPrefix('hello') )
13. 找出字符串出现最多次数的字符以及次数
var str = 'aaabbbbbccddddddddddx';
var obj = {};
for(var i=0;i<str.length;i++){var char = str.charAt(i);if( obj[char] ){obj[char]++;}else{obj[char] = 1;}
}
console.log( obj );
//统计出来最大值
var max = 0;
var word = '';
for( var key in obj ){if( max < obj[key] ){max = obj[key];word = key;}
}
console.log('最多的字符是'+word );
console.log('出现的次数是'+max);
14. new操作符具体做了什么
- 创建一个空的对象
- 将新创建的空对象的原型([[Prototype]])链接到构造函数的原型对象。这意味着新对象可以访问构造函数的原型中定义的属性和方法。
- 用 “this” 关键字引用新创建的空对象,然后调用构造函数,将构造函数中的属性和方法添加到新创建的对象中。
- 如果构造函数没有显式返回一个对象,则返回这个新创建的对象。
function Fun( age,name ){this.age = age;this.name = name;
}
function create( fn , ...args ){//1. 创建了一个空的对象var obj = {}; //var obj = Object.create({})//2. 将空对象的原型,指向于构造函数的原型Object.setPrototypeOf(obj,fn.prototype);//3. 将空对象作为构造函数的上下文(改变this指向)var result = fn.apply(obj,args);//4. 对构造函数有返回值的处理判断return result instanceof Object ? result : obj;
}
console.log( create(Fun,18,'张三') )
15. 闭包
闭包:指那些引用了另一个函数作用域中变量的函数。闭包是一个函数加上到创建函数作用域的链接,闭包关闭了函数的自由变量。
优点: 内部函数可以访问外部函数的变量(解决:把变量手动清空)
缺点: 变量会驻留在内存中,造成内存损耗;在一些低版本浏览器,如ie中还可能造成内存泄漏
16. Promise 的三种状态是什么?如何转换?
-
状态:
pending
(进行中)、fulfilled
(已成功)、rejected
(已失败)。 -
转换规则:
初始状态为 pending。
调用 resolve() 变为 fulfilled,状态不可逆。
调用 reject() 变为 rejected,状态不可逆。
console.log(1);
new Promise((resolve) => {console.log(2);resolve();
}).then(() => {console.log(3);
});
console.log(4);
// 1 2 4 3
// new Promise 的构造函数是同步执行的,会立即输出 1 和 2。
// .then() 是微任务,会在当前宏任务(同步代码)执行完毕后执行,因此输出 4 后再输出 3。
17. Promise.all 和 Promise.race 的区别?
- Promise.all:
接收一个 Promise 数组,所有 Promise 都成功时返回结果数组,若有一个失败则直接返回失败。 - Promise.race:
接收一个 Promise 数组,返回第一个完成(成功或失败)的 Promise 的结果。
18. async/await 的作用是什么?
- 作用:
async
函数返回一个 Promise。
await
用于等待一个 Promise 的结果,使异步代码看起来像同步代码。 - 优势:
代码更易读,避免回调地狱。
支持 try/catch 捕获错误。
async function foo() {console.log(1);await bar();console.log(2);
}function bar() {console.log(3);return new Promise((resolve) => {setTimeout(() => resolve(), 0);});
}foo();
console.log(4);
// 1 3 4 2
// foo() 是异步函数,console.log(1) 同步执行。
// await bar() 会暂停 foo() 的执行,但不会阻塞外部代码,因此先输出 3 和 4。
// 微任务队列中的 resolve() 执行后,foo() 继续执行,输出 2。
19. async/await 和 Promise 的关系?
async/await 是基于 Promise 的语法糖,底层仍依赖 Promise。
await 等待的是一个 Promise 对象,若非 Promise 会被自动包装为 Promise.resolve()。
20. instanceof 和 typeof
- typeof:适合检测基本类型和函数,但不够精确。
- instanceof:适合检测引用类型,但受原型链和跨窗口影响。
- 实际开发中:
基本类型用 typeof 或严格相等(如 === null)。
对象类型用 instanceof 或 Array.isArray() 等专用方法。
复杂场景用 Object.prototype.toString.call()。
21. Map
在 JavaScript 中,Map 是一种用于存储键值对的集合数据结构,于 ES6(ECMAScript 2015)中引入。与传统的对象(Object)不同,Map 的键可以是任意类型(包括对象、函数、基本类型等),并且保留了插入顺序。
核心特性:
- 键的类型灵活:键可以是任意类型(对象、函数、原始值等),而对象的键只能是字符串或 Symbol。
- 插入顺序保留:Map 会按照键值对的插入顺序进行迭代。
- 性能优化:对于频繁增删键值对的场景,Map 的性能通常优于对象。
- 直接访问大小:通过
size
属性直接获取键值对的数量,而对象需要手动计算(如Object.keys(obj).length
)。
Map与Object对比:
22. 箭头函数和普通函数
关于this:
- 普通函数:动态绑定 this,this 的值由调用方式决定(全局对象、DOM 事件、构造函数等)。
const obj = {name: 'Alice',greet: function() {console.log(this.name); // 输出 'Alice'}
};
obj.greet();// 回调函数中可能丢失 this
setTimeout(obj.greet, 100); // 输出 undefined(this 指向全局对象)
- 箭头函数:词法绑定 this:this 继承自外层作用域(定义时的上下文),无法通过 call、apply 或 bind 修改。
const obj = {name: 'Alice',greet: () => {console.log(this.name); // 输出 undefined(this 指向外层作用域,如 window)}
};
obj.greet();// 适合回调中保留 this
const button = document.querySelector('button');
button.addEventListener('click', () => {console.log(this); // 保持外层 this(如 Vue/React 组件实例)
});
构造函数与 new:
- 普通函数:可作为构造函数,通过 new 调用
- 箭头函数:不可作为构造函数,没有 prototype 属性,new 调用会报错
function Person(name) {this.name = name;
}
const alice = new Person('Alice');const Person = () => {};
const alice = new Person(); // 报错:Person is not a constructor
arguments 对象:
- 普通函数:内部可访问 arguments 对象(类数组,包含所有参数)
- 箭头函数:无 arguments 对象,需使用剩余参数(Rest Parameters)
function sum() {return Array.from(arguments).reduce((acc, val) => acc + val, 0);
}
sum(1, 2, 3); // 6const sum = (...args) => args.reduce((acc, val) => acc + val, 0);
sum(1, 2, 3); // 6