大佬教程收集整理的这篇文章主要介绍了2021前端面试js题目总结,不妨看看有没有属于你的那道题,大佬教程大佬觉得挺不错的,现在分享给大家,也给大家做个参考。
<ul id="traffic" class="">
<li id="green"></li>
<li id="yellow"></li>
<li id="red"></li>
</ul>
ul {
position: absolute;
width: 200px;
height: 200px;
top: 50%;
left: 50%;
transform: translate(-50%,-50%);
}
/*画3个圆代表红绿灯*/
ul >li {
width: 40px;
height: 40px;
border-radius:50%;
opacity: 0.2;
display: inline-block;
}
/*执行时改变透明度*/
ul.red >#red,
ul.green >#green,
ul.yellow >#yellow{
opacity: 1.0;
}
/*红绿灯的三个颜色*/
#red {BACkground: red;}
#yellow {BACkground: yellow;}
#green {BACkground: green;}
function timeout(timer){
return function(){
return new Promise(function(resolve,reject){
setTimeout(resolve,timer)
})
}
}
var green = timeout(3000);
var yellow = timeout(4000);
var red = timeout(5000);
var traffic = document.getElementById("traffic");
(function restart(){
'use Strict' //严格模式
console.log("绿灯"+new Date().getSeconds()) //绿灯执行三秒
traffic.className = 'green';
green()
.then(function(){
console.log("黄灯"+new Date().getSeconds()) //黄灯执行四秒
traffic.className = 'yellow';
return yellow();
})
.then(function(){
console.log("红灯"+new Date().getSeconds()) //红灯执行五秒
traffic.className = 'Red';
return red();
}).then(function(){
restart()
})
})();
需要
import axios from 'axios'
const http = ({
url,@H_685_12@method,params,headers
}) => {
return new Promise ( (resolve,reject) => {
axios({
url,
method,
params,
headers
})
.then( res => {
resolve(res)
})
.catch( error => {
throw error
})
})
}
export default http
.catch
setTimeout(function(){
console.log('1')
});
new Promise(function(resolve){
console.log('2');
resolve();
}).then(function(){
console.log('3')
});
console.log('4')
settimeout肯定是异步的。 我也知道有一个event队列c;你settimeout没设置时间应该直接就进入这个队列了吧c;然后就是Promise的回掉函数进入event队列。 当时我二话不说给了个答案 2c;4c;1c;3.并且很自信。然后面试官就问你不想想了?我说不想了。然后后半段他全程开始皱眉头了。我也凉凉。最后他让我回去看一下宏任务和微任务。
首先说一下普通的异步函数的执行过程吧
同步和异步任务分别进入不同的执行"场所"c;同步的进入主线程c;异步的进入Event Table并注册函数。当指定的事情完成时c;Event Table会将这个函数移入Event Queue。主线程内的任务执行完毕为空c;会去Event Queue读取对应的函数c;进入主线程执行。上述过程会不断重复c;也就是常说的Event Loop(事件循环)。
那么如此看来我给的答案还是对的。但是js异步有一个机制c;就是遇到宏任务c;先执行宏任务c;将宏任务放入eventqueuec;然后在执行微任务c;将微任务放入eventqueue最骚的是c;这两个queue不是一个queue。当你往外拿的时候先从微任务里拿这个回掉函数c;然后再从宏任务的queue上拿宏任务的回掉函数。 我当时看到这我就服了还有这种骚操作。
而宏任务一般是:包括整体代码scriptc;setTimeoutc;seTinterval。
微任务:Promisec;process.nextTick。
记住就行了。
然后回到开头的代码。因为Settimeout是宏任务c;虽然先执行的他c;但是他被放到了宏任务的eventqueue里面c;然后代码继续往下检查看有没有微任务c;检测到Promise的then函数把他放入了微任务序列。等到主线进程的所有代码执行结束后。先从微任务queue里拿回掉函数c;然后微任务queue空了后再从宏任务的queue拿函数。
https://juejin.im/post/59e85eebf265da430d571f89
原型链的设计是js的精髓所在c;比较抽象。需要从内部设计原理去理解这种设计思想c;在纸上画画其中的关系会帮助理解。
prototype对象
prototype对象的引入:所有实例对象需要共享的属性和方法c;都放在这个对象中c;那些不需要共享的属性和方法c;就放在构造函数中。以此来模拟类。
function Animal(name) {
this.name = name
}
Animal.prototype.getName = function() {
console.log(this.name)
}
var animal1 = new Animal('kate')
var animal2 = new Animal('Lucy')
//对象animal1 和 animal2共享方法getName
animal1.getName()
animal2.getName()
原型链
在javascript中c;每个对象都有一个指向它的原型(prototypE)对象的内部链接。每个原型对象又有自己的原型c;直到某个对象的原型为null为止c;组成这条链的最后一环。
*proto写入es6标准
当一个对象被创建时c;它的__protp__
属性和内部属性[[prototype]]
指向相同的对象(也就是它的构造函数的prototype
属性)。改变__proto__
属性的值同时也会改变内部属性[[prototype]]
的值c;除非该对象是不可扩展的。 在ES5中c;所有构造函数的__proto__都指向Function.prototype **在ES6中c;构造函数的__proto__指向它的父类构造函数
obj.__proto__ === obj.[[prototype]]
// ES5
Cat.__proto__ === Function.prototype
// ES6
Cat.__proto__ === Animal
构造函数继承
有四种方式可以实现构造函数的继承 1.调用apply方法
function Animal() {
this.species = '动物'
}
Animal.prototype.getName = function() {
console.log('我是动物')
}
function Cat() {
Animal.apply(this, arguments)
}
var cat = new Cat()
cat.species // 动物
cat.getName() // undefined
这种方法可以继承父类构造函数的属性c;但是无法继承prototype
属性c;即父类中共享的方法和属性
2.改写prototype
对象
Cat.prototype = new Animal()
Cat.prototype.constructor = Cat
这是最常用的方法来模拟单继承c;缺点是始终要保留Animal的对象c;如果Animal对象比较大时c;会消耗部分内存(其实很少)c;并且没有实现多继承
3.直接继承prototype
Cat.prototype = Animal.prototype
Cat.prototype.constructor = Cat
缺点是当修改了Cat.prototype上的方法时会影响Animal.prototype
4.利用空对象作中介
var F = function(){}
F.prototype = Animal.prototype
Cat.prototype = new F()
Cat.prototype.constructor = Cat
浏览器是多进程的c;浏览器每一个 tab 标签都代表一个独立的进程(也不一定c;因为多个空白 tab 标签会合并成一个进程)c;浏览器内核(浏览器渲染进程)属于浏览器多进程中的一种。
浏览器内核有多种线程在工作。
GUI 渲染线程:
JS 引擎线程:
事件触发线程:
定时器触发线程:
http 请求线程:
http 请求的时候会开启一条请求线程。
JavaScript 引擎是单线程c;也就是说每次只能执行一项任务c;其他任务都得按照顺序排队等待被执行c;只有当前的任务执行完成之后才会往下执行下一个任务。
HTML5 中提出了 Web-Worker APIc;主要是为了解决页面阻塞问题c;但是并没有改变 JavaScript 是单线程的本质。了解 Web-Worker。
JavaScript 事件循环机制分为浏览器和 Node 事件循环机制c;两者的实现技术不一样c;浏览器 Event Loop 是 HTML 中定义的规范c;Node Event Loop 是由 libuv 库实现。这里主要讲的是浏览器部分。
Javascript 有一个 main thread 主线程和 call-stack 调用栈(执行栈)c;所有的任务都会被放到调用栈等待主线程执行。
JS 调用栈
JS 调用栈是一种后进先出的数据结构。当函数被调用时c;会被添加到栈中的顶部c;执行完成之后就从栈顶部移出该函数c;直到栈内被清空。
同步任务、异步任务
JavaScript 单线程中的任务分为同步任务和异步任务。同步任务会在调用栈中按照顺序排队等待主线程执行c;异步任务则会在异步有了结果后将注册的回调函数添加到任务队列(消息队列)中等待主线程空闲的时候c;也就是栈内被清空的时候c;被读取到栈中等待主线程执行。任务队列是先进先出的数据结构。
Event Loop
调用栈中的同步任务都执行完毕c;栈内被清空了c;就代表主线程空闲了c;这个时候就会去任务队列中按照顺序读取一个任务放入到栈中执行。每次栈内被清空c;都会去读取任务队列有没有任务c;有就读取执行c;一直循环读取-执行的操作c;就形成了事件循环。
前端跨域的方案:
1、通过jsonp跨域
2、postmessage跨域
3、跨域资源共享(CORS)
4、nginx代理跨域
5、nodejs中间件代理跨域
6、WebSocket协议跨域
7.反向代理
<u>https://segmentfault.com/a/1190000011145364</u>
Promisec;就是一个对象c;用来传递异步操作的消息c;避免了层层嵌套的回调函数。它代表了某个未来才会知道结果的事件(通常是一个异步操作)c;并且这个事件提供统一的APIc;可供进一步处理。
(1)对象的状态不受外界影响。有三种状态:Pending(进行中)、Resolved(已完成c;又称Fulfilled)和Rejected(已失败)。
(2)一旦状态改变c;就不会再变c;任何时候都可以得到这个结果。Promise对象的状态改变c;只有两种可能:从Pending变为Resolved和从Pending变为Rejected。只要这两种情况发生c;状态就凝固了c;不会再变了c;会一直保持这个结果
我们创造的每一个函数都有一个prototype(原型)属性。这个属性是一个指针c;指向原型对 象。在默认情况下c;所有的原型对象都会有一个constructor(构造函数)属性c;这个属性包含一个指向prototype属相所在的指针。当调用构造函数创建一个新实例之后c;该实例内部将包含一个指针(内部属性)c;指向构造函数的原型对象。
<u>https://juejin.im/post/5ae95290518825672c00c0a4</u>
1. Promise
2. Generator
3. Async-await
4. Node.js 中的nextTick()和setimmediate()
5. async库
git flow feature start f1 添加新特性c;这个操作创建了一个基于develop的特性分支c;并切换到这个分支之下。
git flow feature finish f1 完成新特性c;这个操作会合并f1分支到develop分支c;并删除特性分支c;切换回develop分支。
git flow feature publish f1 发布新分支c;发布新特性分支到远程服务器c;其它用户也可以使用这分支。
修复bug:
git flow hotfix start VERSION [BASename] 创建hotfix分支c;VERSION 参数标记着修正版本c;[BASename]为finish release时填写的版本号。
一、问题描述:
在一个升序数组中c;使用折半查找得到要查询的值的索引位置。如:
var a=[1,2,3,4,5,6,7,8,9];
search(a,3);//返回2
search(a,1);//左边界c;返回0
search(a,9);//右边界c;返回8
search(a,0);//比最小的值还小c;返回"您查找的数值不存在"
search(a,10);//比最大的值还大c;返回"您查找的数值不存在"
注:折半查找必须在有序数组中才有效c;无序的数组不能实现查找功能。比如:在[10,5,6,7,8,9,20]中查找10c;中间索引位置的值为7c;比较得出7比10小c;因而应该在右子数组中查找c;实际上不可能找到10; 二、我的实现
function search(arr,num) {
var l=arr.length;
var left=0;
var right=l-1;
var center=@H_685_12@math.floor((left+right)/2);
while(left<=l-1&&right>=0){
if (arr[center]==num) return center;
if (left==right) return "您查找的数不存在";
if (arr[center]>num) {
right=center-1;
center=@H_685_12@math.floor((left+right)/2);
}else if (arr[center]<num) {
left=center+1;
center=@H_685_12@math.floor((left+right)/2);
}
}
}
var a=[1,2,3,4,5,6,7,8,9];
console.log(search(a,-2));
说明:
1、基本思路:
每次比较c;如果数组中间索引位置的值比要查找的值大c;就转而在数组中间位置之前的子数组中查找;相反c;如果数组中间索引位置的值比要查找的值大c;就转而在数组中间位置之后的子数组中查找;如果数组中间索引位置的值恰好等于要查找的值c;就返回该索引位置。
2、left定义查找范围的起始位置c;right定义查找范围的结束位置c;center定义查找范围的中间位置。
3、while中的逻辑说明:
(1)由于不知道具体查找查找多少次c;while是比较好的选择;
(2)循环结束条件:
a、一旦当right小于0时c;就不再查找c;再纠缠也不会有结果。例如:在a=[1,2,3,4,5,6,7,8,9]中查找0c;当查找范围变为left=0,right=0,center=0时c;进入while语句c;由于arr[center]>0,故执行
right=center-1;center=Math.floor((left+right)/2);
得到right=-1此时应不再进入循环;
b、一旦当left>l-1时c;就不再查找c;同样再纠缠也不会有结果。例如:在a=[1,2,3,4,5,6,7,8,9]中查找10c;当查找范围变为left=8,right=8,center=8时c;进入while语句c;由于arr[center]<10,故执行
left=center;center=Math.floor((left+right)/2);
得到left=9,此时应不再进入循环;
4、始终是通过center匹配到要查找的值;
5、Math.floor处理了查找范围长度为偶数的情况;
6、当left==right了c;而Arr[center]==num却没执行c;可以得出结论查找不到的;
7、当arr[center]==num时c;整个函数都结束了c;后面语句是不会执行的。
var reg = /1[0-9]$/
push() 方法可向数组的末尾添加一个或多个元素c;并返回新的长度
注释:该方法会改变数组的长度。
语法:
arrayObject.push(newelement1,newelement2,…,newelementX)
参数描述
newelement1 必需。要添加到数组的第一个元素。
newelement2 可选。要添加到数组的第二个元素。
newelementX 可选。可添加多个元素。
push() 方法可把它的参数顺序添加到 arrayObject 的尾部。它直接修改 arrayObjectc;而不是创建一个新的数组。push() 方法和 pop() 方法使用数组提供的先进后出栈的功能。
Undefined、Null、Bollean、number、String
前者会自动转换类型
后者不会
语法结构上不同c;
for 一般用来遍历数组的c;是比较简单的操作
for in 一般用来遍历对象c;虽然for in 也能遍历数组c;但是会存在
以下几个问题:
1、index索引为字符串型数字c;不能直接进行几何运算
2、遍历顺序有可能不是按照实际数组的内部顺序
3、使用for in会遍历数组所有的可枚举属性c;包括原型。例如上栗
的原型方法method和name属性
这也是为什么用for不用for in的区别c;如果是遍历普通数组的话c;
用for是最好的选择c;但是如果是对象c;用for in就好了。
操作数1 == 操作数2c; 操作数1 === 操作数2
双等号==:
(1)如果两个值类型相同c;再进行三个等号(===)的比较
(2)如果两个值类型不同c;也有可能相等c;需根据以下规则进行类型转换在比较:
1)如果一个是nullc;一个是undefinedc;那么相等
2)如果一个是字符串c;一个是数值c;把字符串转换成数值之后再进行比较
三等号===:
(1)如果类型不同c;就一定不相等
(2)如果两个都是数值c;并且是同一个值c;那么相等;如果其中至少一个是NaNc;那么不相 等。(判断一个值是否是NaNc;只能使用isNaN( ) 来判断)
(3)如果两个都是字符串c;每个位置的字符都一样c;那么相等c;否则不相等。
(4)如果两个值都是truec;或是falsec;那么相等
(5)如果两个值都引用同一个对象或是函数c;那么相等c;否则不相等
(6)如果两个值都是nullc;或是undefinedc;那么相等
for in:
1.for...in 语句用于对数组或者对象的属性进行循环操作。
2.for ... in 循环中的代码每执行一次c;就会对数组的元素或者对象的属性进行一次操作。
3.for...in语句以任意顺序遍历一个对象的可枚举属性。对于每个不同的属性c;语句都会被执行。
for :
1.for循环是对数组的元素进行循环c;而不能引用于非数组对象。
第一种:
function uniq(array){
var temp = []; //一个新的临时数组
for(var i = 0; i < array.length; i++){
if(temp.indexOf(array[i]) == -1){
temp.push(array[i]);
}
}
return temp;
}
var aa = [1,2,2,4,9,6,7,5,2,3,5,6,5];
console.log(uniq(aa));
第二种:对象键值法去重
function uniq(array){
var temp = {}, r = [], len = array.length, val, type;
for (var i = 0; i < len; i++) {
val = array[i];
type = typeof val;
if (!temp[val]) {
temp[val] = [type];
r.push(val);
} else if (temp[val].indexOf(type) < 0) {
temp[val].push(type);
r.push(val);
}
}
return r;
}
var aa = [1,2,"2",4,9,"a","a",2,3,5,6,5];
console.log(uniq(aa));
第三种:排序后相邻去除法
function uniq(array){
array.sort();
var temp=[array[0]];
for(var i = 1; i < array.length; i++){
if( array[i] !== temp[temp.length-1]){
temp.push(array[i]);
}
}
return temp;
}
var aa = [1,2,"2",4,9,"a","a",2,3,5,6,5];
console.log(uniq(aa));
第四种:数组下标法
function uniq(array){
var temp = [];
for(var i = 0; i < array.length; i++) {
//如果当前数组的第i项在当前数组中第一次出现的位置是ic;才存入数组;否则代表是重复的
if(array.indexOf(array[i]) == i){
temp.push(array[i])
}
}
return temp;
}
var aa = [1,2,"2",4,9,"a","a",2,3,5,6,5];
console.log(uniq(aa));
第五种:优化遍历数组法
function uniq(array){
var temp = [];
var index = [];
var l = array.length;
for(var i = 0; i < l; i++) {
for(var j = i + 1; j < l; j++){
if (array[i] === array[j]){
i++;
j = i;
}
}
temp.push(array[i]);
index.push(i);
}
console.log(index);
return temp;
}
var aa = [1,2,2,3,5,3,6,5];
console.log(uniq(aa));
第一种:冒泡排序:
var arr = [1,4,-8,-3,6,12,9,8];
function bubbleSort(arr){
for (var i = 0; i < arr.length; i++) {
for (var j = 0; j < arr.length-i-1; j++) {
if(arr[j] > arr[j+1]){
var c = arr[j];
arr[j] = arr[j+1];
arr[j+1] = c;
}
}
}
return arr;
}
console.log(bubbleSort(arr));
快速排序:
var arr = [1,4,-8,-3,6,12,9,8];
function quicksort(arr){
if(arr.length <= 1){
return arr;
}
var middleIndex = Math.floor(arr.length/2);
var middleNum = arr.splice(@H_685_12@middleIndex,1);
var left = [], right = [];
for (var i = 0; i < arr.length; i++) {
if(arr[i] < middleNum){
left.push(arr[i]);
} else {
right.push(arr[i]);
}
}
return quicksort(left).concat(@H_685_12@middleNum, quicksort(right));
}
console.log(quicksort(arr));
选择排序:
var arr = [1,4,-8,-3,6,12,9,8];
function SELEctSort(arr){
for(var i=0;i<arr.length;i++){
//设置当前范围最小值和索引
var min = arr[i];
var minIndex = i;
//在该范围选出最小值
for(var j=i+1;j<arr.length;j++){
if(@H_685_12@min>arr[j]){
min = arr[j];
minIndex = j;
}
}
//将最小值插入,并将原来位置的最小值删除
arr.splice(i,0,@H_685_12@min);
arr.splice(@H_685_12@minIndex+1,1);
}
return arr;
}
console.log(SELEctSort(arr));
插入排序:
var array = [1,4,-8,-3,6,12,9,8];
function SELEctSort(arr){
for(var i=0;i<arr.length;i++){
//设置当前范围最小值和索引
var min = arr[i];
var minIndex = i;
//在该范围选出最小值
for(var j=i+1;j<arr.length;j++){
if(@H_685_12@min>arr[j]){
min = arr[j];
minIndex = j;
}
}
//将最小值插入,并将原来位置的最小值删除
arr.splice(i,0,@H_685_12@min);
arr.splice(@H_685_12@minIndex+1,1);
}
}
SELEctSort(array);
document.write(array);
var arr = [1,4,-8,-3,6,12,9,8];
function bubbleSort(arr){
for (var i = 0; i < arr.length; i++) {
for (var j = 0; j < arr.length-i-1; j++) {
if(arr[j] > arr[j+1]){
var c = arr[j];
arr[j] = arr[j+1];
arr[j+1] = c;
}
}
}
return arr;
}
console.log(bubbleSort(arr));
在谈原型链之前c;我们首先要了解自定义函数与 Function 之间是什么关系c;而构造函数、原型和实例之间又存在什么千丝万缕的关系呢?其实c;所有的函数都是 Function 的实例。在构造函数上都有一个原型属性 prototypec;该属性也是一个对象;那么在原型对象上有一个 constructor 属性c;该属性指向的就是构造函数;而实例对象上有一个 _proto_ 属性c;该属性也指向原型对象c;并且该属性不是标准属性c;不可以用在编程中c;该属性用于浏览器内部使用。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-QqhVpWo7-1630416218424)(en-resource://database/450:1)] [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-8tp8hnPS-1630416218430)(en-resource://database/452:1)]
1、原型链
1)构造函数、原型和实例的关系
①构造函数都有一个属性prototypec;这个属性是一个对象(Object的实例)
②原型对象prototype里面有一个constructor属性c;该属性指向原型对象所属的构造函数
③实例对象都有一个_proto_属性c;该属性也指向构造函数的原型对象c;它是一个非标准属性c;
不可以用于编程c;它是用于浏览器自己使用的
2)prototype与_proto_的关系
①prototype是构造函数的属性
②_proto_是实例对象的属性 ——这两者都指向同一个对象
【总结】
i)函数也是对象c;对象不一定是函数;
ii)对象的本质:无序的键值对集合;键值对当中的值可以是任意数据类型的值
iii)对象就是一个容器c;这个容器当中放的是(属性和方法)
3)属性搜索
①在访问对象的某个成员的时候会先在对象中找是否存在
②如果当前对象中没有就在构造函数的原型对象中找
③如果原型对象中没有找到就到原型对象的原型上找
④知道Object的原型对象的原型是null为止
2、Function——
所有函数都是Function的实例
`①本地对象:独立于宿主环境(浏览器)的对象——包括Object、Array、Date、RegExp、 Function、Error、number、String、Boolean
②内置对象——包括Math、Global(windowc;在js中就是全局变量)c;使用的时候不需要 new
③宿主对象——包括自定义对象、DOM、BOM
第一种: new关键字改变this指向
function Fn(){
this.user = "追梦子";
}
var a = new Fn();
console.log(a.user); //追梦子
用变量a创建了一个Fn的实例(相当于复制了一份Fn到对象a里面)c;此时仅仅只是创建c;并没有执行c;而调用这个函数Fn的是对象ac;那么this指向的自然是对象ac;那么为什么对象a中会有userc;因为你已经复制了一份Fn函数到对象a中c;用了new关键字就等同于复制了一份第二种: call()
第二种: call()
var a = {
user:"追梦子",
fn:function(){
console.log(this.user); //追梦子
}
}
var b = a.fn;
b.call(a); //若不用callc;则b()执行后this指的是Window对象
把b添加到第一个参数的环境中c;简单来说c;this就会指向那个对象。
第三种:apply()
var a = {
user:"追梦子",
fn:function(){
console.log(this.user); //追梦子
}
}
var b = a.fn;
b.apply(a);
第四种:bind()
var a = {
user:"追梦子",
fn:function(){
console.log(this.user);
}
}
var b = a.fn;
b.bind(a); //代码没有被打印
我们发现代码没有被打印c;对c;这就是bind和call、apply方法的不同c;实际上bind方法返回的是一个修改过后的函数。
1. 变量声明
let 与 const:
可以把let看成varc;只是它定义的变量被限定在了特定范围内才能使用c;而离开这个范围则无效。const则很直观c;用来定义常量c;即无法被更改值的变量。
for (let i=0;i<2;i++)console.log(i);//输出: 0,1
console.log(i);//输出:undefined,严格模式下会报错
2.类的支持
ES6中添加了对类的支持c;引入了class关键字(其实class在JavaScript中一直是保留字c;目的就是考虑到可能在以后的新版本中会用到c;现在终于派上用场了)。JS本身就是面向对象的c;ES6中提供的类实际上只是JS原型模式的包装。现在提供原生的class支持后c;对象的创建c;继承更加直观了c;并且父类方法的调用c;实例化c;静态方法和构造函数等概念都更加形象化。
//类的定义
class Animal {
//ES6中新型构造器
constructor(name) {
this.name = name;
}
//实例方法
sayName() {
console.log('My name is '+this.name);
}
}
//类的继承
class ProgrAMMer extends Animal {
constructor(name) {
//直接调用父类构造器进行初始化
super(name);
}
program() {
console.log("I'm coding...");
}
}
//测试我们的类
var animal=new Animal('dummy'),
wayou=new ProgrAMMer('wayou');
animal.sayName();//输出 ‘My name is dummy’
wayou.sayName();//输出 ‘My name is wayou’
wayou.program();//输出 ‘I'm coding...’
3.字符串模板
字符串模板相对简单易懂些。ES6中允许使用反引号 ` 来创建字符串c;此种方法创建的字符串里面可以包含由美元符号加花括号包裹的变量${vraiblE}。如果你使用过像C#等后端强类型语言的话c;对此功能应该不会陌生。
//产生一个随机数
var num=@H_685_12@math.random();
//将这个数字输出到console
console.log(`your num is ${num}`);
4.解构:
自动解析数组或对象中的值。比如若一个函数要返回多个值c;常规的做法是返回一个对象c;将每个值做为这个对象的属性返回。但在ES6中c;利用解构这一特性c;可以直接返回一个数组c;然后数组中的值会自动被解析到对应接收该值的变量中。
var [x,y]=getVal(),//函数返回值的解构
[name,,age]=['wayou','male','secrect'];//数组解构
function getVal() {
return [ 1, 2 ];
}
console.log('x:'+x+', y:'+y);//输出:x:1, y:2
console.log('name:'+name+', age:'+age);//输出: name:wayou, age:secrect
5.Promise:
Promises是处理异步操作的一种模式c;之前在很多三方库中有实现c;比如jQuery的deferred 对象。当你发起一个异步请求c;并绑定了.when(), .done()等事件处理程序时c;其实就是在应用promise模式。
//创建promise
var promise = new Promise(function(resolve, reject) {
// 进行一些异步或耗时操作
if ( /*如果成功 */ ) {
resolve("stuff worked!");
} else {
reject(Error("It broke"));
}
});
//绑定处理程序
promise.then(function(result) {
//promise成功的话会执行这里
console.log(result); // "stuff worked!"
}, function(err) {
//promise失败会执行这里
console.log(err); // Error: "It broke"
});
ES6提供的解决异步处理方法
有两个优点
1.promise对象的状态不受外界影响
-pending 初始状态
-fulfilled 成功状态
-rejected 失败状态
2.promise的状态一旦改变c;就不会再变c;状态不可逆c;只能由pending变成pending变成fulfilled或者由pending变成rejected
三个缺点
1.无法取消promisec;一旦新建它就会立即执行c;无法中途取消
2.如果不设置回调函数c;promise内部抛出的错误c;不会反映到外部
3.当处于pending状态时c;无法得知目前进展到哪一个阶段
用法
const promise = new Promise(function(resolve, reject) {
// ... some code
if (/* 异步操作成功 */){
resolve(@R_489_7548@;
} else {
reject(error);
}
});
同源策略是浏览器的一个安全功能c;不同源的客户端脚本在没有明确授权的情况下c;不能读写对方资源。同源指的是协议c;域名和端口号均相同则属于同源
使用jsonp跨域c;因为Script标签引入的js是不受同源策略的限制c;通过script标签引入一个js或者是一个其他后缀形式(如php,jsp等)的文件c;此时文件返回一个JS函数的调用
通过cors跨域c;实现cors通信的关键是服务器c;只要服务器实现cors接口c;就可以跨域
<script type="text/javascript">
var xhr = new XMLhttprequest();
xhr.open("c;GET", "http://segmentfault.com/u/trigkit4/",truE);
xhr.send();
</script>
反向代理跨域c;反向代理指的是在前端的服务器环境中c; 短暂的开启一个后端服务器c; 由后端服务器进行数据请求c; 然后在将结果返回给前端
模块化的开发方式可以提高代码复用率c;方便进行代码的管理。通常一个文件就是一个模块c;有自己的作用域c;只向外暴露特定的变量和函数。目前流行的js模块化规范有CommonJS、AMD、CMD以及ES6的模块系统
1、AMD推崇依赖前置c;在定义模块的时候就要声明其依赖的模块 2、CMD推崇就近依赖c;只有在用到某个模块的时候再去require 这种区别各有优劣c;只是语法上的差距c;而且requireJS和SeaJS都支持对方的写法
AMD和CMD最大的区别是对依赖模块的执行时机处理不同c;注意不是加载的时机或者方式不同
highcharts http://www.highcharts.com/
jscharts http://www.jscharts.com/
AdminLTE http://adminlte.la998.com/
库和框架都是一种有别于软件、面向程序开发者的产品形式。
库是将代码集合成的一个产品c;供程序员调用。面向对象的代码组织形式而成的库也叫
类库。
框架则是为解决一个(一类)问题而开发的产品c;框架用户一般只需要使用框架提供的类
或函数c;即可实现全部功能。
柯里化(英语:Currying)c;又称为部分求值c;是把接受多个参数的函数变换成接受一个单一参数(最初函数的第一个参数)的函数c;并且返回一个新的函数的技术c;新函数接受余下参数并返回运算结果。
具体内容见 https://juejin.im/entry/58b316d78d6d810058678579
1)意外的全局变量引起的内存泄露
```javascript
function leak () {
leak="xxx"; //leak成为一个全局变量c;不会被回收 相当于 window.leak = 'xxx'
}
```
2. 闭包可以维持函数内局部变量c;使其得不到释放。
3. 没有清理的DOM元素引用
4. 被遗忘的定时器或者回调
1. 定义局部变量.查找局部变量比全局变量要快。
2. 不滥用闭包。
3. 合并js文件,减少http请求
4. 避免使用for-in循环
5. 尽量不用withc;eval语句c;try-catch的catch子句要谨慎使用
1. 闭包经典使用场景一:通过循环给页面上多个dom节点绑定事件
```javascript
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
</head>
<body>
<button>Button0</button>
<button>Button1</button>
<button>Button2</button>
<button>Button3</button>
<button>Button4</button>
</body>
</html>
for(var i = 0, len = btns.length; i < len; i++) {
(function(i) {
btns[i].onclick = function() {
alert(i);
}
}(i))
}
```
2. 封装变量 闭包可以将一些不希望暴露在全局的变量封装成“私有变量”。
3. 闭包使用场景三:延续局部变量的寿命
闭包工具库:???
301 redirect: 301 代表永久性转移(PeRMANently Moved)
302 redirect: 302 代表暂时性转移(Temporarily Moved )
详细来说c;301和302状态码都表示重定向c;就是说浏览器在拿到服务器返回的这个状态码后会自动跳转到一个新的URL地址c;这个地址可以从响应的LOCATIOn首部中获取(用户看到的效果就是他输入的地址A瞬间变成了另一个地址B)——这是它们的共同点。他们的不同在于。301表示旧地址A的资源已经被永久地移除(这个资源不可访问了)c;搜索引擎在抓取新内容的同时也将旧的网址交换为重定向之后的网址;302表示旧地址A的资源还在(仍然可以访问)c;这个重定向只是临时地从旧地址A跳转到地址Bc;搜索引擎会抓取新的内容而保存旧的网址。
举例:
```javascript
console.log (a) //由于变量提升c;输出undefined
var a
```
```javascript
console.log(a) //报错 ReferenceError: a is not defined
let a
```
ES6规定c;let/const 命令会使区块形成封闭的作用域。若在声明之前使用变量c;就会报错。
总之c;在代码块内c;使用 let 命令声明变量之前c;该变量都是不可用的。
这在语法上c;称为 “暂时性死区”( temporal dead zonec;简称 TDZ)。
var fn=function(resolve, reject){
console.log('begin to execute!');
var number=@H_685_12@math.random();
if(number<=0.5){
resolve('less than 0.5');
}else{
reject('greater than 0.5');
}
}
var p=new Promise(fn);
p.then(function(data){
console.log('Resolve: ', data);
}, function(data){
console.log('Reject: ', data);
})
对promise源码的理解:
当我们运行 var p=new Promise(fn) 这条语句的时候c;fn函数就已经执行了c;然而c;p.then这个方法是在后面才定义了resolve和rejectc;那么为何fn函数能够知道resolve和reject函数是什么呢?换句话说c;resolve和reject函数是如何回到过去c;出现在先执行的fn函数当中的呢?要解决这个问题c;主要运用的就是setTimeout这个方法c;来延迟fn当中resolve和reject的执行。我们知道js是单线程+消息队列c;必须等主线程代码执行完毕才能开始执行消息队列当中的代码。因此c;会首先执行then这个方法c;给里面两个参数赋值。
加入状态:pending, resolved, rejected
在Promise规范当中c;规定Promise只能从初始pending状态变到resolved或者rejected状态c;是单向变化的c;也就是说执行了resolve就不会再执行rejectc;反之亦然。并在必要的地方进行判断c;防止重复执行。
function @H_834_166@myPromise(fn) {
this.value;
this.status = 'pending';
this.resolveFunc = function() {};
this.rejectFunc = function() {};
fn(this.resolve.bind(this), this.reject.bind(this));
}
@H_630_305@myPromise.prototype.resolve = function(val) {
var self = this;
if (this.status == 'pending') { //判断状态
this.status = 'Resolved';
this.value=val;
setTimeout(function() {
self.resolveFunc(self.value);
}, 0);
}
}
@H_630_305@myPromise.prototype.reject = function(val) { //判断状态
var self = this;
if (this.status == 'pending') {
this.status = 'Rejected';
this.value=val;
setTimeout(function() {
self.rejectFunc(self.value);
}, 0);
}
}
@H_630_305@myPromise.prototype.then = function(resolveFunc, rejectFunc) {
this.resolveFunc = resolveFunc;
this.rejectFunc = rejectFunc;
}
链式调用:
要实现链式调用c;then方法的返回值也必须是一个Promise对象c;这样才能再次在后面调用then。
async
c;await
关键字只能在使用async
定义的函数中使用。任何一个async
函数都会隐式返回一个promise
c;并且promise resolve 的值就是 return 返回的值await
then
、catch
等等)c;操作本身的语义反而不容易看出来。(1) 箭头函数与function定义函数的写法:
//function
function fn(a, b){
return a + b;
}
//arrow function
var foo = (a, b)=>{ return a + b };
(2) this的指向:
使用function定义的函数c;this的指向随着调用环境的变化而变化的c;而箭头函数中的this指向是固定不变的c;一直指向的是定义函数的环境。
(3) 构造函数
function是可以定义构造函数的c;而箭头函数是不行的。
(4) 变量提升
由于js的内存机制c;function的级别最高c;而用箭头函数定义函数的时候c;需要var(let const定义的时候更不必说)关键词c;而var所定义的变量不能得到变量提升c;故箭头函数一定要定义于调用之前!
(1) 由于箭头函数不绑定thisc; 它会捕获其所在(即定义的位置)上下文的this值c; 作为自己的this值
(2)方法的箭头函数this指向全局window对象c;而普通函数则指向调用它的对象,箭头函数没有this
因为箭头函数没有自己的thisc;它的this其实是继承了外层执行环境中的thisc;且this指向永远不会随在哪里调用、被谁调用而改变c;所以箭头函数不能作为构造函数使用c;或者说构造函数不能定义成箭头函数c;否则用new调用时会报错!
箭头函数没有自己的arguments对象。在箭头函数中访问arguments实际上获得的是外层局部(函数)执行环境中的值。可以在箭头函数中使用rest参数代替arguments对象c;来访问箭头函数的参数列表
解构赋值c;即对某种结构进行解析c;然后将解析出来的值赋值给相关的变量c;常见的有数组、对象、字符串的解构赋值等
数组解构 只要等号两边的模式相同c;左边的变量就会被赋予对应的值。
let [foo, [[bar], baz]] = [1, [[2], 3]];
foo // 1
bar // 2
baz // 3
对象结构
对象的解构与数组有一个重要的不同。数组的元素是按次序排列的c;变量的取值由它的位置决定;而对象的属性没有次序c;变量必须与属性同名c;才能取到正确的值。
let { bar, foo } = { foo: 'aaa', bar: 'bbb' };
foo // "aaa"
bar // "bbb"
字符串结构
字符串被转换成了一个类似数组的对象
const [a, b, c, d, e] = 'hello';
a // "h"
b // "e"
c // "l"
d // "l"
e // "o"
负载
概念
扩展运算符(spread)是三个点(…)。它好比 rest 参数的逆运算c;将一个数组转为用逗号分隔的参数序列。扩展运算符与正常的函数参数可以结合使用c;后面也可以放置表达式c;但如果后面是一个空数组c;则不产生任何效果。
let arr = [];
arr.push(...[1,2,3,4,5]);
console.log(arr); //[1,2,3,4,5]
console.log(1, ...[2, 3, 4], 5) //1 2 3 4 5
console.log(...(1 > 0 ? ['a'] : [])); //a
console.log([...[], 1]); //[1]
应用
1 替代函数的apply方法 由于扩展运算符可以展开数组c;所以不再需要apply方法c;将数组转为函数的参数了。
// ES5 的写法
Math.@H_834_166@max.apply(null, [14, 3, 77])
// ES6 的写法
Math.@H_834_166@max(...[14, 3, 77])
2 复制数组
// ES5 的写法
const a1 = [1, 2];
const a2 = a1.concat();
// ES6 的写法
const a1 = [1, 2];
onst a2 = [...a1]; //或 const [...a2] = a1;
3 合并数组
// ES5 的写法
[1, 2].concat(@H_685_12@more);
arr1.concat(arr2, arr3);
// ES6 的写法
[1, 2, ...@H_685_12@more];
[...arr1, ...arr2, ...arr3]
4 与结构赋值结合
// ES5 的写法
a = list[0], rest = list.slice(1)
// ES6 的写法
[a, ...rest] = list
答:对数组进行深拷贝:
答:深拷贝
答:有两种方法:
方法一: Set + Array.from()
var set1 = Array.from(new Set([1,1,2,2,33,'33',44,'44'
]))
方法二: …[拓展运算符] + Set
var tt = [...new Set([5,5,6,6,8,])] // 5c;6c;8
答: 判断数组方法:
1.用instanceof判断
使用instanceof运算符可以分辨数组和对象c;可以判断数组是数组。
2.用constructor判断
实例化的数组拥有一个constructor属性c;这个属性指向生成这个数组的方法。 当constructor属性被修改之后c;就无法用这个方法判断数组是数组了
toString方法将会返回"[object type]"c;其中的type代表的是对象的类型c;根据type的值c;我们就可以判断这个疑似数组的对象到底是不是数组了
4.用Array对象的isArray方法判断 isArray方法返回truec;当参数不为数组的时候c;isArray方法返回false
typeof 检测数组返回值
typeof是javascript原生提供的判断数据类型的运算符c;它会返回一个表示参数的数据类型的字符串
答:
思路:
//统计一个数组中有多少个不重复的单词:
// 不用reduce时:
var arr = [@R_675_1091@,"orange",@R_675_1091@,"orange","pear","orange"];
function getWordCnt(){
var obj = {};
for(var i= 0, l = arr.length; i< l; i++){
var item = arr[i];
obj[item] = (obj[item] +1 ) || 1;
}
return obj;
}
console.log(getWordCnt());//{apple: 2, orange: 3, pear: 1}
// 用reduce时:
var arr = [@R_675_1091@,"orange",@R_675_1091@,"orange","pear","orange"];
function getWordCnt(){
return arr.reduce(function(prev,next){
prev[next] = (prev[next] + 1) || 1;
return prev;
},{});
}
console.log(getWordCnt());//{apple: 2, orange: 3, pear: 1}
数组名.push("元素1","元素2",...)
数组名.pop()
数组名.join("连接符")
数组名.shift()
数组名.unshift("元素1","元素2",...)
数组名.slice(start,end)
注:splice是直接修改原数组c;而slice不会修改原数组。数组名.splice(start,deleteCount,item1,item2,...)
参数说明: start:必选项c;表示从数组中剪切的起始位置下标索引号。 deteleCount:必选项c;表示从数组中切取的元素个数。 item:可选项c;表示切取时插入原数组切入点开始出的一个或多个元素。用法:对数组的元素进行排序 arr.sort()直接操作原有数组c;返回原有数组c;当没有传比较函数的话不会按大小排序c;而是按顺序排序
var arr = [22,12,3,43,56,47,4];
arr.sort();
console.log(arr); // [12, 22, 3, 4, 43, 47, 56]
arr.sort(function (m, n) {
if (m < n) return -1
else if (m > n) return 1
else return 0
});
console.log(arr); // [3, 4, 12, 22, 43, 47, 56]
- 概要
Canvas 位图c;是需要自己画点的白板;
WebGL 3D位图c;是基于 Canvas 的 3D 框架。
- 用途
Canvas 适用于位图c;高数据量高绘制频率(帧率)的场景c;如动画、游戏;
WebGL 主要用来做 3D 展示、动画、游戏。
- canvas缺点:
- 只能绘制2D图像c;暂时不支持3D图像。
-canvas绘制图形出并非可以直接操作的dom对象。如果要对其进行类似dom的操作c;例如添加属性等等c;比较麻烦(这就是为什么必须使用类库)。
- canvas优点:
- 由于canvas绘图不会给每个点生成对象c;所以绘制速度快c;消耗内存少。(这点主要是相对于SVGc;VML技术而言)
- 兼容性较好。除了IE6c;其他浏览器都可以支持。(IE7c;8需要载入扩展JSc;终究还是能用的)
- webGL
是一项使用JavaScript实现3D绘图的技术c;浏览器无需插件支持c;Web开发者直接使用js调用相关API就能借助系统显卡(GPU)进行编写代码从而呈现3D场景和对象。
BOM:浏览器对象模型(Brower Object Model)c;是用于操作浏览器而出现的APIc;BOM对象则是Javascript对BOM接口的实现。
BOM提供了独立于内容的、可以与浏览器窗口进行交互的对象结构。通过BOM对象可以访问浏览器功能部件和属性。
BOM中代表浏览器窗口的window对象是Javascript顶层对象c;其他BOM对象均为window对象的子对象。被作为window对象的属性来引用。
其他BOM对象都是在window对象中进行操作。
DOM 是文档对象模型c;比如 html 是树结构的c;操作 dom 就是操作这颗树:
DOM:文档对象模型(Document Object Model)c;是W3C定义的一套用于处理HTML和XML文档内容的标准编程接口API。javascript实现DOM接口的对象对应的是document对象c;JS通过该对象来对HTML/XML文档进行增删改查。DOM定义了HTML和XML的逻辑结构c;将整个页面划分成由层次节点构成的文档c;以树的形式来展现c;如上面那张图所示。
在BOM和DOM结构层次图中c;document对象属于window对象c;所以DOM也可以看作是BOM的一部分
0-9 ↩︎
以上是大佬教程为你收集整理的2021前端面试js题目总结,不妨看看有没有属于你的那道题全部内容,希望文章能够帮你解决2021前端面试js题目总结,不妨看看有没有属于你的那道题所遇到的程序开发问题。
如果觉得大佬教程网站内容还不错,欢迎将大佬教程推荐给程序员好友。
本图文内容来源于网友网络收集整理提供,作为学习参考使用,版权属于原作者。
如您有任何意见或建议可联系处理。小编QQ:384754419,请注明来意。