第1节 原生js面试题#
摘讲目录
14 什么是工厂模式,有什么优缺点 *
22 原型 ,原型链,继承的方式 ***
32 h5新特性 ***
35 队列和栈 *
50 手写防抖、节流,防抖和节流的区别 ***
01 map和foreach的区别#
-
定义#
- foreEach()方法: 针对每一个元素执行提供的函数。
- map()方法: 创建一个新的数组,其中每一个元素由调用数组中的每一个元素执行提供的函数得来。
-
相同点#
- 都是对数组的遍历
- 没有办法终止或者跳出forEach()循环,除非抛出异常;
-
区别#
- forEach()方法不会返回执行结果,而是undefined,允许callback更改原始数组的元素。
- 而map()方法会得到一个新的数组并返回。
- forEach()的执行速度 < map()的执行速 JS中Map和ForEach的区别 - 简书
02 解释下JavaScript中this是如何工作的。#
或问: 谈谈this的指向 第一准则: this永远指向函数运行时所在的对象,而不是函数被创建时所在的对象。
- 普通的函数调用,函数被谁调用,this就是谁。
- 匿名函数或不处于任何对象中的函数指向window 。
- 如果是call,apply,bind,指定的this是谁,就是谁。
- 构造函数的话,如果不用new操作符而直接调用,那即this指向window。用new操作符生成对象实例后,this就指向了新生成的对象。
- 箭头函数的this, 由于箭头函数不绑定this, 它会捕获其所在(即定义的位置)上下文的this值, 作为自己的this值
前端面试题 | JS部分(附带答案)_axgrfetia063590468的博客-CSDN博客http://docs.huruqing.cn/JavaScript/02.html#_2-%E8%B0%88%E8%B0%88this%E7%9A%84%E6%8C%87%E5%90%91
03 异步线程,轮询机制,宏任务微任务#
js中的异步以及事件轮询 (event loop) 机制_毅江的博客-CSDN博客js 宏任务和微任务 - 栴檀 - 博客园详解JavaScript中的Event Loop(事件循环)机制 - 知乎
04 阻止冒泡 **#
w3c方法是event.stopPropagation() IE中方法是window.event.cancelBubble = true
05 阻止默认事件 **#
event.preventDefault() return false; //写在函数的最后一句
06 怎样判断array 和 object ***#
对数组和对象使用typeof,得出的结果都是"object"
- 使用instanceof进行判断,A instanceof B即对象A是不是构造函数B构造出来的,是即返回true, 不是即返回false.
-
在typeof上进行enhancement, typeof数组和对象返回都是object, 但是数组的长度为0及0以上的整数,object的长度为undefined
-
利用Object.prototype.toString.call()方法将该变量转化为代表其类型的string
-
Array.isArray()
-
利用constructor属性进行判断
07 数据类型的隐式转换 **#
做项目时常常涉及到这三类转换
- 其它类型转数字: 详细举例
- 其它类型转布尔: 详细举例
- 其它类型转字符串: 详细举例
08 说一说盒子模型#
-
定义#
- 所有HTML元素可以看作盒子,在CSS中,"box model"这一术语是用来设计和布局时使用。
- CSS盒模型本质上是一个盒子,封装周围的HTML元素,它包括:外边距(margin)、边框(border)、内边距(padding)、实际内容(content)四个属性。
-
区别#
- 标准盒模型:
- 盒子实际内容(content)的width/height=我们设置的width/height;
- 盒子总宽度/高度=width/height+padding+border+margin
- IE盒子模型(怪异盒模型)
- 浏览器的 width 属性不是内容的宽度,而是内容、内边距和边框的宽度的总和;
- 盒子的(content)宽度+内边距padding+边框border宽度 = 我们设置的width(height也是如此),
- 盒子总宽度/高度 = width/height + margin = 内容区宽度/高度 + padding + border + margin。
- 标准盒模型:
-
CSS3可以指定盒子模型种类#
box-sizing: border-box; // 应用怪异盒模型 box-sizing: content-box;// 默认选选项, 应用标准盒模型
09 算法#
一个数组【000000000111111111111】这样,你怎么找出第一个1,最底层的js原生写法, 列如第一个数与第二个数作比较,当第二个大于第一个元素的时候就找出了第一个1,那还有没有更效率的写法
使用循环或递归来实现
10 对象的key能是数字吗? *#
可以是数字,object对应的key没有限制,只是如果是数字,取值的时候就不能用英文句号(.),只能用[]的方式取值。
11 async await 和promise和generator有什么区别#
-
相同点:#
这几种都可以解决异步操作的地狱回调问题
-
async await 和promise对象的区别:#
- async和promise都是异步方法
- 区别是async生成的结果是promise对象,async是promise的终结版。
- await只能在async中使用,await是阻塞的意思,就是暂停,你一起调用2个接口,第一个执行完,不输出结果,要等最第二个接口执行完,才返回这两个的结果。是属于将异步操作转化为同步操作的做法
-
async await和generator:#
- async是Generator的语法糖 ,也就是说,async是对generator的实现,而generator代码更复杂。
- generator 除了能解决回调地域问题,还可以作为迭代器使用。generator 语法一般用于redux-saga redux-saga 一般在umi或dva框架中(一个集成了redux、redux-saga、react-router-redux、react-router的框架)使用
12 手写promise ***#
-
promise的用法#
var promiseObj = new Promise(function (resolve, reject) {
let offer = false;
setTimeout(() => {
if (offer) {
resolve({
msg: '上班去',
company: 'xxxx公司'
})
} else {
reject({
msg: '继续面试'
})
}
},1000);
});
// 获取promiseObj保存的数据
promiseObj.then(function(res){
console.log('成功的信息:',res);
},function(err){
console.log('失败的信息:',err);
})
-
以下是一个简单的promise实现, 想更深一步研究, 请看末尾的链接#
<script>
function MyPromise(callback) {
// 状态
this.status = "pending";
this.successFn;
this.errorFn;
// 处理成功的信息
this.resolve = (data) => {
// 修改状态
this.status = 'fulfilled';
// 在这里把成功的数据返回给.then的第一个参数
this.successFn && this.successFn(data);
};
// 处理失败的信息
this.reject = (data) => {
// 在这里把失败的数据返回给.then第二个参数
this.status = 'rejected';
this.errorFn && this.errorFn(data);
};
/**
* @param {*} successFn 成功的回调
* @param {*} errorFn 失败的回调
*/
this.then = (successFn, errorFn) => {
// 把传过来的两个函数保存起来, 供后面成功或失败的时候调用
this.successFn = successFn;
this.errorFn = errorFn;
};
callback(this.resolve, this.reject);
}
var promiseObj = new MyPromise(function (resolve, reject) {
let offer = true;
setTimeout(() => {
if (offer) {
resolve({
msg: "上班去",
company: "xxxx公司",
});
} else {
reject({
msg: "继续面试",
});
}
}, 1000);
});
promiseObj.then(
function (res) {
console.log("res", res);
},
function (err) {
console.log("err", err);
}
);
</script>
原文地址: 手写Promise - 实现一个基础的Promise - SegmentFault 思否
13 promise.all作用 ***#
- Promise.all(iterable) 方法返回一个 Promise 实例,此实例在 iterable 参数内所有的 promise 都“完成(resolved)”或 参数中不包含 promise 时回调完成(resolve);如果参数中 promise 有一个失败(rejected),此实例回调失败(reject), 失败的原因是第一个失败 promise 的结果。
- 它通常在启动多个异步任务并发运行并为其结果创建承诺之后使用,以便人们可以等待所有任务完成。
- 通俗的说法:解决promise的同步调用问题,可以将多个异步操作转为同步操作,缺点:只要有一个promise返回错误,那么后面的promise都不会再执行
const promise1 = Promise.resolve(3);
const promise2 = 42;
const promise3 = new Promise((resolve, reject) => {
setTimeout(resolve, 100, 'foo');
});
Promise.all([promise1, promise2, promise3]).then((values) => {
console.log(values);
});
14 什么是工厂模式,有什么优缺点 *#
-
定义#
- 工厂模式是一种设计模式,目的是为了创建对象,它通常在类或类的静态方法中实现,
- 有以下目的:当创建相似对象是执行重复操作
- 当编译时,不知道具体类型的情况下,为工厂客户提供一个创建对象的接口
-
优缺点#
优点: 写好了工厂方法后,只要原料足够,我们可以无限次的创建新的对象 缺点:
- 没有使用 new , 而是直接调用方法进行创建
- 每个对象都有自己的函数(像下面的例子中, 每创建一个对象, 就会创建两个函数, 若创建10个对象,就需要创建20个函数, 而这些函数都一模一样的, 完全没必要),浪费资源
//使用工厂方式创建一个对象
function createCat(name, sex) {
//1.原料
var obj = new Object();
//2.加工 -- 对属性进行赋值
obj.name = name;
obj.sex = sex;
//3.方法
obj.showName = function () {
console.log("小猫名称:" + this.name);
};
obj.showSex = function () {
console.log("小猫性别:" + this.sex);
};
//4.加工结束,--输出返回
return obj;
}
//工厂模式的使用
var cat1 = createCat("小花", "母的");
//调用工厂里的方法
cat1.showName();
cat1.showSex();
var cat2 = createCat("罗小黑", "公的");
//调用工厂里的方法
cat2.showName();
cat2.showSex();
接触过哪些设计模式: 工厂模式 、单例模式(数据库连接)、发布-订阅模式观也叫察者模式(双向绑定原理)、mvc模式、mvvm模式
// 发布订阅模式
1. 定义了对象间的一种一对多的依赖关系,当一个对象的状态发 生改变时,所有依赖于它的对象都将得到通知
2. JS中的事件就是经典的发布-订阅模式的实现(观察者甲和乙都订阅了body的点击事件, 当事件发布(被点击)时,它们都收到了通知
// 订阅
document.body.addEventListener('click', observer1, false);
document.body.addEventListener('click', observer2, false);
function observer1() {
console.log('我是观察者甲');
}
function observer2() {
console.log('我是观察者乙');
}
// 发布
document.body.click(); // click1 click2
15 图片/文件夹上传到后台是什么类型? *#
图片上传后台有三种格式:
-
file格式 (创建formData来完成file上传)#
var formData = new FormData();
$.each($("#imgfile")[0].files, function (i, file) {
formData.set("idcard", file); //idcard 字段 根据自己后端接口定
});
//processData: false, contentType: false,多用来处理异步上传二进制文件。
that.$indicator.open();
$.ajax({
url: "http://wjtest.jecinfo.cn:9090/indentity/check",
type: "POST",
data: formData, // 上传formdata封装的数据
....
});
-
base64格式#
<input type="file" id="imgfile">
<script>
var base64Pic = "";
document.getElementById("imgfile").onchange = function () {
var fileReader = new FileReader();
fileReader.readAsDataURL(this.files[0]);
fileReader.onload = function () {
base64Pic = fileReader.result;
console.log(base64Pic); //base64 可以直接放在src上 预览
};
};
</scrip>
-
Blob流格式#
16 原生基础数据类型和引用数据类型的区别 *#
学过数据结构的同学对于栈和堆都多少有点了解吧!不了解也没关系,学习JS中的栈和堆也一样的_,下面我们来看看栈和堆到底是何方神圣......
-
栈和堆#
栈(stack):
栈会自动分配内存空间,会自动释放,存放基本类型,简单的数据段,占据固定大小的空间。基本类型:String,Number,Boolean,Null,Undefined 堆(heap):
动态分配的内存,大小不定也不会自动释放,存放引用类型,指那些可能由多个值构成的对象,保存在堆内存中,包含引用类 型的变量,实际上保存的不是变量本身,而是指向该对象的指针。引用类型:Function,Array,Object
-
区别#
栈:所有在方法中定义的变量都是放在栈内存中,随着方法的执行结束,这个方法的内存栈也自然销毁。
- 优点:存取速度比堆快,仅次于直接位于CPU中的寄存器,数据可以共享;
- 缺点:存在栈中的数据大小与生存期必须是确定的,缺乏灵活性。
堆:
- 堆内存中的对象不会随方法的结束而销毁,即使方法结束后,这个对象还可能被另一个引用变量所引用(参数传递)。
- 创建对象是为了反复利用,这个对象将被保存到运行时数据区。 JS中的栈和堆 - 简书
17 深浅拷贝:#
-
浅拷贝/深度拷贝的区别#
-
定义: 深拷贝和浅拷贝最根本的区别在于是否真正获得一个对象的复制实体,而不是引用。
-
**浅拷贝:**仅仅是指向被复制的内存地址(即只拷贝对象的引用),如果原对象发生改变,那么浅拷贝复制出来的对象也会相应的改变。
-
深拷贝:在内存的堆中开辟一块新的空间用来存放复制的对象, 也就是得到了一个新的对象
-
-
浅拷贝例子#
var obj = { name:'wsscat', age:0 } var obj2 = obj; obj2['c'] = 5; console.log(obj);//Object {name: "wsscat", age: 0, c: 5} console.log(obj2);Object {name: "wsscat", age: 0, c: 5} var arr1 = [1,2,3,4]; var arr2 = arr1; arr2.push(5); console.log(arr1); // [1,2,3,4,5] console.log(arr2); // [1,2,3,4,5]
-
深拷贝(只拷贝第一层)#
深拷贝又分为第一层拷贝和完全拷贝, 只拷贝第一层的深拷贝适用于对象/数组的属性/成员都是基本数据类型
// (1)for循环拷贝对象第一层 var obj = {a:1,b:2,c:3} var obj2 = {}; for (var key in obj) { obj2[key] = obj[key]; } // (2) 扩展运算符拷贝对象第一层 var obj = {a:2,b:3:c:4}; var obj2 = [...obj]; // (3) slice数组拷贝第一层 var arr1 = ["前端","安卓","苹果"]; var arr2 = arr1.slice(0); arr2[0] = "后端"; console.log("原始值:" + arr1 );//前端,安卓,苹果 console.log("新值:" + arr2);//后端,安卓,苹果 // (4) concat数组拷贝第一层 var arr1 = ["前端","安卓","苹果"]; var arr2 = arr1.concat(); arr2[0] = "后端"; console.log("原始值:" + arr1 );//前端,安卓,苹果 console.log("新值:" + arr2);//后端,安卓,苹果
-
深拷贝(完全拷贝)#
// (1)JSON(对象和数组都适用) var obj = {a:2,b:3:c:4}; var str = JSON.stringfy(obj); var obj2 = JSON.parse(str); // (2)for循环完全拷贝 function deepClone(obj){ let objClone = Array.isArray(obj)?[]:{}; if(obj && typeof obj==="object"){ for(key in obj){ if(obj.hasOwnProperty(key)){ //判断ojb子元素是否为对象,如果是,递归复制 if(obj[key]&&typeof obj[key] ==="object"){ objClone[key] = deepClone(obj[key]); }else{ //如果不是,简单复制, 递归调用(自己调用自己) objClone[key] = obj[key]; } } } } return objClone; } let arr = [1,2,{a:2}]; let arr2 = deepClone(arr); arr2[2].a=100; arr[2].a; // 2,arr和arr2是两个完全不同的数组, 相互不会受影响 let obj = { a:1, b: {x:1} } let obj2 = deepClone(obj); obj2.b.x = 100; obj.b.x; // 1 完全复制, 两个相互不受影响
JS数组的深拷贝和浅拷贝_可乐6666的博客-CSDN博客_js数组深拷贝和浅拷贝【JS】深拷贝与浅拷贝的区别,实现深拷贝的几种方法 - 听风是风 - 博客园
18 弹性布局。浮动布局 table布局 *#
- 弹性布局:使用 百分比、rem 、flex 自适应布局
- 浮动布局:使用 float clear
- table布局:一般用于后台显示
- 自适应布局和响应式布局:一般需要用到media媒体查询完成,需要根据屏幕宽度的不同,写多个css样式文件
19 flex 弹性盒子#
-
的水平居中和垂直居中
水平:justify-content:center; 垂直:align-content:center;
-
flex属性
- flex:1 的值是1 1 0%,【父控件有剩余空间占1份放大,父控件空间不足按1缩小,自身的空间大小是0%】 ***
- flex属性 是 flex-grow、flex-shrink、flex-basis三个属性的缩写。 推荐使用此简写属性,而不是单独写这三个属性。
-
flex-grow:定义项目的的放大比例; 默认为0,即 即使存在剩余空间,也不会放大; 所有项目的flex-grow为1:等分剩余空间(自动放大占位); flex-grow为n的项目,占据的空间(放大的比例)是flex-grow为1的n倍。
-
flex-shrink:定义项目的缩小比例; 默认为1,即 如果空间不足,该项目将缩小; 所有项目的flex-shrink为1:当空间不足时,缩小的比例相同; flex-shrink为0:空间不足时,该项目不会缩小; flex-shrink为n的项目,空间不足时缩小的比例是flex-shrink为1的n倍。
-
flex-basis: 定义在分配多余空间之前,项目占据的主轴空间(main size),浏览器根据此属性计算主轴是否有多余空间, 默认值为auto,即 项目原本大小; 设置后项目将占据固定空间。 所以flex属性的默认值为:0 1 auto (不放大会缩小) flex为none:0 0 auto (不放大也不缩小) flex为auto:1 1 auto (放大且缩小)
20 闭包是什么,闭包形成的原因和闭包的用途 *#
闭包定义: 闭包就是能够读取其他函数内部变量的函数。 闭包形成的原因: 当某个函数的作用域链还引用着其他函数的活动对象时,就会形成闭包。 闭包的应用场景:
- 封装私有变量
- 私有变量(私有属性)是后台语言经常用的东西, 意思是这个属性是私有的,外部是无法访问这个变量的
- 如果需要访问也必须通过指定的方法来进行获取和修改
<script>
function getCat() {
// 私有变量name,一般前面加_来表示私有
var _name = '白猫';
return {
getName: function () {
return _name;
},
setName: function (newName) {
_name = newName;
}
}
}
var cat = getCat();
let name = cat.getName(); // 白猫
console.log('name',name);
// 修改猫名
cat.setName('黑猫');
let newName = cat.getName(); // 黑猫
console.log('newName',newName);
</script>
- 模仿块级作用域 (ES5 中没有块级作用域)
<script>
// es6没出来之前,用var定义变量存在变量提升问题
for (let i = 0; i < 10; i++) {
console.info(i)
}
alert(i) // 变量提升,弹出10//为了避免i的提升可以这样做
(function () {
for (var i = 0; i < 10; i++) {
console.info(i)
}
})()
alert(i); // 会报错
// 根据以下代码,说出打印结果
for (var i = 0; i < 10; i++) {
setTimeout(() => {
console.log(i);
}, i * 100);
} // 结果是打印了10个10,并不是我们希望0-9,要想实现我们期待的结果可以使用闭包实现
for (var i = 0; i < 10; i++) {
(function (j) {
setTimeout(() => {
console.log(j);
}, j * 100);
})(i);
}
/***上面的代码,循环了10次,创建了10个函数,每个函数传入的值都不一样* setTimeout里面的函数是个闭包,它的外层函数的变量j,因为闭包缘故,没有被销毁,打印的时候可以取到它的值*/
</script>
- 实现 JS 的模块
<script>
function Common(window) {
var DEBUG = "debug";
function log(args) {
console.log(args);
}
/** debug 利用闭包 */
function debug(args) {
console.log(DEBUG + args);
}
/** 编写 */
function write(args) {
document.write(args);
}
return {
log: log,
debug: debug,
write: write
};
}
//调用
var common = Common(window);
common.log("121");
common.debug(12232);
common.write("dadsa");
</script>
使用闭包的注意点:
- 由于闭包会使得函数中的变量都被保存在内存中,内存消耗很大,所以不能滥用闭包,否则会造成网页的性能问题
- 闭包会在父函数外部,改变父函数内部变量的值。所以,如果你把父函数当作对象(object)使用,把闭包当作它的公用方法(Public Method),把内部变量当作它的私有属性(private value),这时一定要小心,不要随便改变父函数内部变量的值。 js之闭包是如何产生的?
21 什么是内存泄漏,都什么情况容易产生内存泄漏 **#
-
内存泄漏:#
即为变量内容持续占用内存空间,不释放,垃圾回收机制也没有将该内存回收的情况
-
内容泄露的原因:#
- 死循环
- 定时器没有清除
- 绑定的事件没有解除
- 递归调用没有结束条件
-
主要存在内存泄漏的问题点:#
- BOM DOM对象泄漏
- scipt中存在对BOM DOM对象的引用
- javaScript对象泄漏
- 闭包函数导致的泄漏 主要关注的代码点
- DOM中的addEventLisner 函数及派生的事件监听, 比如Jquery 中的on 函数, vue 组件实例的 $on 函数,第三方库中的初始化函数
- BOM对象的事件监听,比如webSocket的监听事件
- 避免不必要的函数引用
- 如果是要render函数,避免在html标签中DOM BOM事件
22 原型 ,原型链,继承的方式 ***#
-
原型#
-
每一个构造函数都有一个属性prototype, 这个prototype属性指向了一个对象, 而这个对象正是用该构造函数创建出来的实例的原型, 而实例都会从原型继承属性, (你可以把原型理解成实例的"父亲", 括号内容不用背).
-
每一个原型都有一个属性constructor, 指向了该构造函数
-
每个实例都有一个内部指针
__proto__
指向了原型<script> // 实例讲解 function Cat(name, age) { this.name = name; this.age = age; } // 1.Cat有一个属性prototype, 是一个对象 console.log(typeof Cat.prototype); // object // 2.每一个原型都有一个一个属性constructor指向了构造函数, 可以使用dir来查看 // dir(Cat); Cat.prototype.constructor === Cat; // true // 小红的丈夫的妻子是小红, 小红的丈夫的妻子的丈夫的妻子的丈夫.... // 3.每一个实例都有一个内部指针__proto__指向了原型, 实例继承了原型的属性 var cat = new Cat('白猫',2); cat.__proto__ === Cat.prototype; Cat.prototype.from = '黑龙江'; console.log(cat); // 实例cat并没有from属性 console.log(cat.from); // from属性是继承了protype的属性 console.log(cat); </script>
-
-
原型链,背诵#
-
每个实例对象(object)都有一个私有属性(称之为
__proto__
)指向它的构造函数的原型对象(prototype) -
该原型对象也有一个自己的原型对象(
__proto__
) -
层层向上直到一个对象的原型对象为
null
根据定义,null
没有原型,并作为这个原型链中的最后一个环节。 -
几乎所有 JavaScript 中的对象都是位于原型链顶端的Object的实例 (Object.prototype就是终极原型)
-
结论: 当我们访问一个实例的属性时, 会先查找该实例由没有这个属性, 没有的话就会到它的原型去查找, 还没有就会一层一层的往上查找, 一直找到Object的原型为止, 有就返回, 没有就返回undefined.
// 实例讲解 // 上面的第1点,第2点,第3点 let p1 = cat.__proto__; let p2 = p1.__proto__; let p3 = p2.__proto__; // null, p2是就是原型链的最后一环 // 第4点 cat.__proto__.__proto__ === Object.prototype; let arr = [1,2,3]; arr.__proto__.__proto__ === Object.prototype; let date = new Date(); date.__proto__.__proto__ === Object.prototype; let obj = {a:2}; // obj的构造函数就是Object本身,所以 obj.__proto__ === Object.prototype;
-
-
原型链继承#
让一个构造函数的prototyp等于另外一个构造函数创建的实例就实现了原型链继承
<script> Animal.prototype.say = function () { console.log('我是动物'); } function Animal() { this.type = '动物'; } // 创建动物的实例 let animal = new Animal(); // 让Cat的原型指向animal,就实现了继承,即Cat创建出来的实例也能拥有Animal及其原型的属性 Cat.prototype = animal; function Cat(name, age) { this.name = name; this.age = age; } var cat = new Cat(); // 实例cat继承了Animal的type属性 console.log(cat.type); // 实例cat继承了Animal原型上的方法 cat.say(); </script>
-
-
原型链继承
-
借用构造函数继承
-
组合式继承
-
寄生式继承
-
寄生组合式继承
-
es6 class继承(理解)
-
使用extends进行继承
-
子类使用super关键字
子类必须在constructor方法中调用super方法,否则新建实例时会报错。这是因为子类自己的this对象,必须先通过父类的构造函数完成塑造,得到与父类同样的实例属性和方法,然后再对其进行加工,加上子类自己的实例属性和方法。如果不调用super方法,子类就得不到this对象。
<script> class Father { constructor(name) { this.name = name; } sayName() { console.log(this.name); } } class Child extends Father { constructor(name, age) { super(name); this.age = age; } } var child = new Child('Tom', 22); child.sayName(); </script>
-
-
23 js异步轮询机制#
- 所有同步任务都在主线程上执行,形成一个执行栈(execution context stack)。
- 主线程之外,还存在一个"任务队列"(task queue)。只要异步任务有了运行结果,就在"任务队列"之中放置一个事件。
- 一旦"执行栈"中的所有同步任务执行完毕,系统就会读取"任务队列",看看里面有哪些事件。那些对应的异步任务,于是结束等待状态,进入执行栈,开始执行。
- 主线程不断重复上面的第三步。只要主线程空了,就会去读取"任务队列",这就是JavaScript的运行机制。这个过程会不断重复。
- "任务队列"中的事件,除了IO设备的事件以外,还包括一些用户产生的事件(比如鼠标点击、页面滚动等等)。只要指定过回调函数,这些事件发生时就会进入"任务队列",等待主线程读取。
- 事件循环(event loop)
主线程从"任务队列"中读取事件,这个过程是循环不断的,所以整个的这种运行机制又称为Event Loop(事件循环)。
<script>
function test() {
var startTime = Date.now();
setTimeout(() => {
var endTime = Date.now();
console.log('时间过去了:', endTime - startTime, 'setTimeout开始执行');
}, 1000);
var count = 1000000000;
for (;;) {
count--;
if (count === 0) {
console.log("时间到");
break;
}
}
console.log(1);
console.log(2);
console.log(3);
}
test();
</script>
24 使用node.js 创建本地服务器 *#
25 Git常用命令 *#
- git init 初始化 *
- git clone 克隆代码库 *
- git config 配置
- git add 增加文件到暂存区 *
- git commit 提交暂存区到仓库 *
- git branch 名称 新建分支 *
- git checkout 切换分支 *
- git merge 合并分支 *
- git branch -d 删除分支 *
- git tag 打tag 包 *
- git status 查看状态 *
- git log 查看日志 *
- git diff 查看暂存区和工作区差异
- git fetch 下载远程仓库的变动 *
- git pull 取回远程仓库变化,并与本地分支合并 *
- git push 上传本地指定分支到远程仓库 *
26 跨域的解决方案 *#
-
定义#
- 知其然知其所以然,在说跨域方法之前,我们先了解下什么叫跨域,浏览器有同源策略,只有当“协议”、“域名”、“端口号”都相同时,才能称之为是同源,其中有一个不同,即是跨域。
- 那么同源策略的作用是什么呢?同源策略限制了从同一个源加载的文档或脚本如何与来自另一个源的资源进行交互。这是一个用于隔离潜在恶意文件的重要安全机制。
- 那么我们又为什么需要跨域呢?一是前端和服务器分开部署,接口请求需要跨域,二是我们可能会加载其它网站的页面作为 iframe 内嵌。
-
跨域的几种方式#
- jsonp
- jsonp缺点: 只能是get方式不能是post方式
- jsonp原理:script标签的src属性可以跨域
- proxy代理, 下面这些这些都是属性使用proxy代理进行跨域的例子
- vue(实际是webpack)的devServer的proxy设置(只能是打包前使用,打包后需要后台配置跨域)
- nginx反向代理
- node使用中间件设置跨域
- 后台头部设置 Access-Control-Allow-Origin ,cors
- 其他 方式: a. document.domain + iframe (只有在主域相同的时候才能使用该方法) b. location.hash + iframe c. window.name + iframe d. postMessage(HTML5中的XMLHttpRequest Level 2中的API) e. web socket web sockets是一种浏览器的API,它的目标是在一个单独的持久连接上提供全双工、双向通信。
- jsonp
27 for循环定时器打印产生问题,怎么解决 **#
<script>
for (var i = 1; i <= 5; i++) {
setTimeout(function () {
console.log(i);
}, 1000);
}
</script>
打印结果为5个6;
- 原因是:js是单线程,有任务队列,任务队列分为两种,同步任务和异步任务。
- 同步任务指的是,在主线程上排队执行的任务,只有前一个任务执行完毕,才能执行后一个任务;
- 异步任务指的是,不进入主线程、而进入"任务队列"(task queue)的任务,只有"任务队列"通知主线程,某个异步任务可以执行了,该任务才会进入主线程执行。
- 任务执行顺序: 所以循环是同步任务先执行,当执行完之后,当setTimeout的异步操作准备好后,通知主线程,主线程将其从异步队列中取出来,再执行,所以当循环完成时,i的值位6,setTimeout再执行,打印5个6
28 http协议详解 http请求方式有 http响应状态码 *#
-
http 特点:#
- 支持客户/服务器模式。
- 简单快速:。
- 灵活:
- 无连接:
- 无状态:
-
http请求由三部分组成,分别是:请求行、消息报头、请求正文 请求方法(所有方法全为大写)有多种,各个方法的解释如下: GET 请求获取Request-URI所标识的资源 POST 在Request-URI所标识的资源后附加新的数据 HEAD 请求获取由Request-URI所标识的资源的响应消息报头 PUT 请求服务器存储一个资源,并用Request-URI作为其标识 DELETE 请求服务器删除Request-URI所标识的资源 TRACE 请求服务器回送收到的请求信息,主要用于测试或诊断 CONNECT 保留将来使用 OPTIONS 请求查询服务器的性能,或者查询与资源相关的选项和需求
-
在接收和解释请求消息后,服务器返回一个HTTP响应消息。#
HTTP响应也是由三个部分组成,分别是:状态行、消息报头、响应正文 3xx:重定向--要完成请求必须进行更进一步的操作 4xx:客户端错误--请求有语法错误或请求无法实现 5xx:服务器端错误--服务器未能实现合法的请求
301 永久移动,请求的资源被永久的移动到新url,返回信息会包括新的url。浏览器会自动定向到新url 302 临时移动,资源只是临时被移动,客户端赢继续使用原有url 304 未修改,所请求的资源未修改,服务器返回此状态码是=时,不会返回任何资源,客户端通常会缓存访问过的资源,通过提供一个头信息指出客户端希望只返回在指定日期之后修改的资源
400 Bad Request //客户端请求有语法错误,不能被服务器所理解,解决方法:修改请求的参数及语法 401 Unauthorized //请求未经授权,这个状态代码必须和WWW-Authenticate报头域一起使用 , 解决方式:即没有启用任何认证方式,只需要在IIS Manager里面启用需要的认证方式即可。 即被Authorization Rule阻挡,则需要分析这个authorization rule是否是否必须来决定对他的更改。 403 Forbidden //服务器收到请求,但是拒绝提供服务 1. 你的IP被列入黑名单。 2、你在一定时间内过多地访问此网站(一般是用采集程序),被防火墙拒绝访问了。 3、网站域名解析到了空间,但空间未绑定此域名。 4、你的网页脚本文件在当前目录下没有执行权限。 6、以http方式访问需要ssl连接的网址。 7、浏览器不支持SSL 128时访问SSL 128的连接。 8、在身份验证的过程中输入了错误的密码。
解决方式:
1、重建dns缓存,对于一些常规的403 forbidden错误,马海祥建议大家首先要尝试的就是重建dns缓存,在运行中输入 cmd,然后输入ipconfig /flushdns即可。如果不行的话,就需要在hosts文件里把主页解析一下了。 2、修改文件夹安全属性 3、关于apache导致的403 forbidden错误,需设置Apache的配置文件。 4、关于HawkHost空间出现403 Forbidden错误需设置htaccess文件。
404 Not Found //请求资源不存在,eg:输入了错误的URL 解决办法:输入正确的url地址 405 请求方式不对 ,比如原本需要post方式请求的,你写了get方式请求
29 一个页面从发送http请求到渲染页面的全过程(http事务流程)***#
- 域名解析 --> 2.发起TCP的3次握手 --> 3.建立TCP连接后发起http请求 --> 4.服务器响应http请求,浏览器得到html代码 --> 5.浏览器解析html代码,并请求html代码中的资源(如js、css、图片等) --> 6.浏览器对页面进行渲染呈现给用户 从输入URL到页面加载的全过程 - 小火柴的蓝色理想 - 博客园
30.1 tcp和udp的区别 *#
TCP与UDP区别总结:
-
TCP面向连接(如打电话要先拨号建立连接);UDP是无连接的,即发送数据之前不需要建立连接
-
TCP提供可靠的服务。也就是说,通过TCP连接传送的数据,无差错,不丢失,不重复,且按序到达;UDP尽最大努力交付,即不保证可靠交付
-
TCP面向字节流,实际上是TCP把数据看成一连串无结构的字节流;UDP是面向报文的UDP没有拥塞控制,因此网络出现拥塞不会使源主机的发送速率降低(对实时应用很有用,如IP电话,实时视频会议等)
-
每一条TCP连接只能是点到点的;UDP支持一对一,一对多,多对一和多对多的交互通信
-
TCP首部开销20字节;UDP的首部开销小,只有8个字节6、TCP的逻辑通信信道是全双工的可靠信道,UDP则是不可靠信道
30.2 什么是长连接 *#
在HTTP/1.0中默认使用短连接。也就是说,客户端和服务器每进行一次HTTP操作,就建立一次连接,任务结束就中断连接。 当客户端浏览器访问的某个HTML或其他类型的Web页中包含有其他的Web资源(如JavaScript文件、图像文件、CSS文件等), 每遇到这样一个Web资源,浏览器就会重新建立一个HTTP会话。 而从HTTP/1.1起,默认使用长连接,用以保持连接特性。使用长连接的HTTP协议,会在响应头加入这行代码: Connection:keep-alive 在使用长连接的情况下,当一个网页打开完成后,客户端和服务器之间用于传输HTTP数据的TCP连接不会关闭,客户端再次访问这个服务器时 ,会继续使用这一条已经建立的连接。Keep-Alive不会永久保持连接,它有一个保持时间,可以在不同的服务器软件(如Apache)中设定这个 时间。实现长连接需要客户端和服务端都支持长连接。
31 ajax是同步还是异步,ajax的流程 *#
ajax是异步的, 流程: (1)创建XMLHttpRequest对象,也就是创建一个异步调用对象. (2)创建一个新的HTTP请求,并指定该HTTP请求的方法、URL及验证信息 (3)设置响应HTTP请求状态变化的函数. (4)发送HTTP请求 .(sned) (5)获取异步调用返回的数据.(onreadystatechange) (6)使用JavaScript和DOM实现局部刷新.
32 h5新特性 ***#
-
语义化标签#
-
Canvas绘图 ***#
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Document</title> <style> canvas { border: 1px solid; } </style> </head> <body> <canvas id="myCanvas" width="600" height="600"></canvas> <script> var c = document.getElementById("myCanvas"); var cxt = c.getContext("2d"); //获取2d作图对象 cxt.moveTo(10, 10); //画线的的起始点 cxt.lineTo(150, 50); //画线 cxt.lineTo(10, 50); cxt.stroke(); //线 cxt.fillStyle = "#FF0000"; //填充颜色 cxt.beginPath(); //开始路径 cxt.arc(70, 18, 15, 0, Math.PI * 2, true); //画圆 cxt.closePath(); //结束路径 cxt.fill(); //填充 var img = document.createElement('img'); img.src = 'https://www.w3school.com.cn/i/eg_dragdrop_w3school.gif'; img.onload = function () { cxt.drawImage(img, 200, 200); //画布填充图片 } </script> </body> </html>
-
SVG绘图#
-
地理定位 *#
可以使用百度地图, 腾讯地图或者高德地图提交的公共api完成地图地位和地图绘制
-
拖放API *#
- draggable="true" 讲需要被拖放的数据加上此属性 - setData()保存数据 - drag ondrag 当拖动元素或选中的文本时触发。 - dragend ondragend 当拖拽操作结束时触发 (比如松开鼠标按键或敲“Esc”键). (见结束拖拽) - dragenter ondragenter 当拖动元素或选中的文本到一个可释放目标时触发(见 指定释放目标)。 - dragexit ondragexit 当元素变得不再是拖动操作的选中目标时触发。 - dragleave ondragleave 当拖动元素或选中的文本离开一个可释放目标时触发。 - dragover ondragover 当元素或选中的文本被拖到一个可释放目标上时触发(每100毫秒触发一次)。 - dragstart ondragstart 当用户开始拖动一个元素或选中的文本时触发(见开始拖动操作)。 - drop ondrop 当元素或选中的文本在可释放目标上被释放时触发(见执行释放)。
// 拖放例子 <!DOCTYPE HTML> <html> <head> <style type="text/css"> #div1 { width: 198px; height: 66px; padding: 10px; border: 1px solid #aaaaaa; } </style> </head> <body> <p>请把 W3School 的图片拖放到矩形中:</p> <div id="div1" ondrop="drop(event)" ondragover="allowDrop(event)"></div> <br /> <img id="drag1" src="https://www.w3school.com.cn/i/eg_dragdrop_w3school.gif" draggable="true" ondragstart="drag(event)" /> <script type="text/javascript"> function allowDrop(ev) { ev.preventDefault(); } // 拖放时把元素id存起来 function drag(ev) { ev.dataTransfer.setData("Text", ev.target.id); } // 放下时, 获取元素id,并根据id获取元素,并把元素插入到目标元素 function drop(ev) { ev.preventDefault(); var data = ev.dataTransfer.getData("Text"); var $ele = document.getElementById(data); ev.target.appendChild($ele); } </script> </body> </html>
-
Web Storage * (浏览器缓存)#
*1.特性 *Cookie* 一般由服务器生成,可设置失效时间,(设置过期时间是小于当前时间)。 如果在浏览器端生成Cookie,默认是关闭浏览器后失效;大小 4K左右 每次都会携带在HTTP头中,如果使用cookie保存过多数据会带来性能问题 需要程序员自己封装,源生的Cookie接口不友好
*localStorage* 除非被清除,否则永久保存大小 5M 仅在客户端(即浏览器)中保存,不参与和服务器的通信 源生接口可以接受,亦可再次封装来对Object和Array有更好的支持
*sessionStorage* 仅在当前会话下有效,关闭页面或浏览器后被清除 大小 5M 仅在客户端(即浏览器)中保存,不参与和服务器的通信 源生接口可以接受,亦可再次封装来对Object和Array有更好的支持
-
COOKIE是如何工作的?
Cookie可用于客户端数据的存储,在没有其它存储办法时,使用这种方式是可行的,但随着现在浏览器开始支持各种各样 的存储方式而逐渐被废弃。 由于服务器指定Cookie以后浏览器的每次请求都会携带Cookie数据,这会带来额外的性能负 担(尤其是在移动环境下)。 新的浏览器API已经允许开发者直接在本地存储数据,如可以使用Web storage API (本地 存储和会话存储)和IndexedDB(索引数据库)。
- COOKIE主要用在以下三个方面: 会话状态管理(如用户登录状态、购物车) 个性化设置(如用户自定义设置) 浏览器行为跟踪(如跟踪分析用户行为)
-
COOKIE的缺陷 每个 HTTP 请求中都包含 Cookies,从而导致传输相同的数据减缓我们的 Web 应用程序。 每个 HTTP 请求中都包含 Cookies,从而导致发送未加密的数据到互联网上,可能会导致数据泄露,虽然进行过加密,但是攻击者拿到cookie后仍然可以登录,因为难以识别是否为同一个用户在登陆。 Cookies 只能存储有限的 4KB 数据,对于复杂的存储需求来说是不够用的。
-
sessionStroage有哪些应用场景? 答: 在不需要和服务器交互的场所,用来存储用户数据之类的,可以在路由页跳转的时候取出更改储存,减少调用接口的次数,减轻服务器压力。
-
用storage怎么来判断用户是否需要再登陆? 答:可以用加密的方法存储,每次用户访问的时候可以取出调用服务器接口作为参数发送进行对比,存在账号密码就直接跳过登录页。
-
localStorage是否可以在同源窗口共享? 答:同一浏览器的相同域名和端口的不同页面间可以共享相同的 localStorage,但是不同页面间无法共享sessionStorage的信息。
-
-
WebSocket#
它的最大特点就是,服务器可以主动向客户端推送信息,客户端也可以主动向服务器发送信息,是真正的双向平等对话,属于服务器推送技术的一种。(支付、客服模块可以使用这个技术)
32 css3新特性 *** 动画效果#
-
css3选择器#
-
css新增样式属性#
border-radius border-image background-size background-origin background-clip:规定背景的绘制区域。 linear-gradient():线性渐变。 radial-gradient():径向渐变。 word-break:定义如何换行。 word-wrap:允许长的内容可以自动换行。 text-overflow:指定当文本溢出包含它的元素,应该发生什么。 text-shadow:文字阴影。 transform-style:指定嵌套元素是怎样在三维空间中呈现。 2D转换方法 rotate(angle):定义 2D 旋转,在参数中规定角度。 translate(x,y):指定元素在二维空间中的位移。X轴方向向右,Y轴方向向下。 scale(n):定义 2D 缩放转换。 matrix(a,b,c,d,e,f):定义 2D 转换,使用六个值的矩阵。 skew(x-angle,y-angle):定义沿着 X 和 Y 轴的 2D 倾斜转换。 perspective(n):为 3D 转换元素定义透视视图。 translate:指定元素在的位移。 matrix3d(n,n,n,n,n,n,n,n,n,n,n,n,n,n,n,n):定义 3D 转换,使用 16 个值的 4x4 矩阵。 过渡 transition:设置元素当过渡效果,四个简写属性为: * animation:为元素添加动画,是一个简写属性。 *
-
css新增的一些伪类#
1、p:first-of-type 选择器匹配属于其父元素的特定类型的首个子元素的每个元素。 2、p:last-of-type 选择器匹配属于其父元素的特定类型的最后一个子元素的每个元素。 3、p:only-of-type 选择器匹配属于其父元素的特定类型的唯一子元素的每个元素。 4、p:only-child 选择器匹配属于其父元素的唯一子元素的每个元素。 5、p:nth-child(2) 选择器匹配属于其父元素的第 N 个子元素,不论元素的类型。n 可以是数字、关键词或公式。 6、:enabled :disabled表单控件的禁用状态。 7、:checked:checked 选择器匹配每个选中的输入元素(仅适用于单选按钮或复选框)。 Flex弹性布局 * 媒体查询 media *
33 es6新特性 *#
let const * 变量的解构赋值 *
<script>
let [a, b, c] = [1, 2, 3];
let [head, ...tail] = [1, 2, 3, 4];
let { foo, bar } = { foo: "aaa", bar: "bbb" };
</script>
字符串扩展
let text='abcdefg';
for (let i of text){
console.log(i);
}
includes():返回布尔值,表示是否找到了参数字符串。
startsWith():返回布尔值,表示参数字符串是否在源字符串的头部。
endsWith():返回布尔值,表示参数字符串是否在源字符串的尾部。
repeat方法返回一个新字符串,表示将原字符串重复n次。
padStart()用于头部补全,
padEnd()用于尾部补全。
模板字符串 `aaa{$test}`
数值的扩展:
Number.isFinite()用来检查一个数值是否为有限的(finite)。
Number.isNaN()用来检查一个值是否为NaN。
Number.parseInt(),转整型
Number.parseFloat()转浮点型
Number.isInteger()用来判断一个值是否为整数
Math.trunc方法用于去除一个数的小数部分,返回整数部分。
Math.sign方法用来判断一个数到底是正数、负数、还是零。
Math.cbrt方法用于计算一个数的立方根。
Math.sign()用来判断一个值的正负,但是如果参数是-0,它会返回-0。ES2016 新增了一个指数运算符(**)。
数组的扩展:
(1)Array.from方法用于将两类对象转为真正的数组:
类似数组的对象(array-like object)
可遍历(iterable)的对象(包括ES6新增的数据结构Set和Map)。
(2)Array.of方法用于将一组值,转换为数组。
(3)Array.prototype.copyWithin(target, start = 0, end = this.length)数组实例的copyWithin方 法,在当前数组内部,将指定位置的成员复制到其他位置(会覆盖原有成员),然后返回当前数组。也就是说,使用 这个方法,会修改当前数组。
(4)数组实例的find方法,用于找出第一个符合条件的数组成员。它的参数是一个回调函数,所有数组成员依次执行该回 调函数,直到找出第一个返回值为true的成员,然后返回该成员。如果没有符合条件的成员,则返回undefined。
(5)fill方法使用给定值,填充一个数组。
(6)entries(),keys()和values()——用于遍历数组。它们都返回一个遍历器对象
(详见《Iterator》一章),可以用for...of循环进行遍历,唯一的区别是keys()是对键名的遍历、values() 是对键值的遍历,entries()是对键值对的遍历。Array.prototype.includes方法返回一个布尔值,表示某个 数组 是否包含给定的值,与字符串的includes方法类似。该方法属于ES7,但Babel转码器已经支持。
函数的扩展:
函数参数的默认值
与解构赋值默认值结合使用
函数的 length 属性
指定了默认值以后,函数的length属性,将返回没有指定默认值的参数个数。也就是说,指定了默认值后,length属性将失真
ES6 引入 rest 参数(形式为“...变量名”),用于获取函数的多余参数,这样就不需要使用arguments对象了。rest 参数搭配的变量是一个数组,该变量将多余的参数放入数组中。
扩展运算符 ... *****
替代数组的apply方法
// ES5的写法
Math.max.apply(null, [14, 3, 77])
// ES6的写法
Math.max(...[14, 3, 77])
// 等同于
Math.max(14, 3, 77);
扩展运算符的应用:合并数组 与解构赋值结合 函数的返回值 字符串 实现了Iterator接口的对象 Map和Set结构,Generator函数
/************/
严格模式 'use strict';
重点掌握,重点重点
箭头函数 ES6允许使用“箭头”(=>)定义函数。 箭头函数可以保留this的指向 *****
箭头函数注意点:
(1)函数体内的this对象,就是定义时所在的对象,而不是使用时所在的对象。
(2)不可以当作构造函数,也就是说,不可以使用new命令,否则会抛出一个错误。
(3)不可以使用arguments对象,该对象在函数体内不存在。如果要用,可以用Rest参数代替。
(4)不可以使用yield命令,因此箭头函数不能用作Generator函数。
对象的扩展:
1. Object.is() 比较两个值是否相等
2. Object.assign方法用于对象的合并,将源对象(source)的所有可枚举属性,复制到目标对象(target)。
属性的遍历
3. ES6一共有5种方法可以遍历对象的属性。
(1)for...in
for...in循环遍历对象自身的和继承的可枚举属性(不含Symbol属性)。
(2)Object.keys(obj)
Object.keys返回一个数组,包括对象自身的(不含继承的)所有可枚举属性(不含Symbol属性)。
(3)Object.getOwnPropertyNames(obj)
Object.getOwnPropertyNames返回一个数组,包含对象自身的所有属性(不含Symbol属性,但是包括不可枚举 属性)。
(4)Object.getOwnPropertySymbols(obj)
Object.getOwnPropertySymbols返回一个数组,包含对象自身的所有Symbol属性。
(5)Reflect.ownKeys(obj)
Reflect.ownKeys返回一个数组,包含对象自身的所有属性,不管是属性名是Symbol或字符串,也不管是否可枚 举。
4. Object.setPrototypeOf方法的作用与__proto__相同,用来设置一个对象的prototype对象,返回参数对象本 身。它是 ES6 正式推荐的设置原型对象的方法。该方法与Object.setPrototypeOf方法配套,用于读取一个对象的 原型对象。
5. Object.keys()引入了Object.keys方法,返回一个数组,成员是参数对象自身的(不含继承的)所有可遍历 (enumerable)属性的键名。
6. Object.values方法返回一个数组,成员是参数对象自身的(不含继承的)所有可遍历(enumerable)属性的键 值。
7. Object.entries方法返回一个数组,成员是参数对象自身的(不含继承的)所有可遍历(enumerable)属性的键值对数组。
set 和 map 数据类型 *
set可以用于数组去重,是无序的,set中不包含重复的元素
map 的key值可以是数组对象字符串等格式
Map类似于一个对象,是键值对的集合,键值可以是字符串,各种类型,对象。
proxy ***
Proxy 可以理解成,在目标对象之前架设一层“拦截”,外界对该对象的访问,都必须先通过这层拦截,因此提供了一种机制,可以对外界的访问进行过滤和改写。Proxy 这个词的原意是代理,用在这里表示由它来“代理”某些操作,可以译为“代理器”
var obj = new Proxy({}, {
get: function (target, key, receiver) {
console.log(`getting ${key}!`);
return Reflect.get(target, key, receiver);
},
set: function (target, key, value, receiver) {
console.log(`setting ${key}!`);
return Reflect.set(target, key, value, receiver);
}
});
vue双向绑定原理的Object.defineProperty 更改为了 Proxy,性能有所优化
promise *
解决异步回调地狱问题
var promise = new Promise(function(resolve, reject) {
// ... some code
if (/* 异步操作成功 */){
resolve(value);
} else {
reject(error);
}
});
.then() Promise实例添加状态改变时的回调函数。
.catch() 用于指定发生错误时的回调函数。
Promise.all方法用于将多个Promise实例,包装成一个新的Promise实例。
all中的内容是两个异步操作,只有等到它们的结果都返回了,才会触发pickTopRecommentations这个回调函数。
Promise.race方法同样是将多个Promise实例,包装成一个新的Promise实例。
Promise.resolve()有时需要将现有对象转为Promise对象,Promise.resolve方法就起到这个作用。
Promise.reject(reason)方法也会返回一个新的 Promise 实例,该实例的状态为rejected。
.done()Promise对象的回调链,不管以then方法或catch方法结尾,要是最后一个方法抛出错误,都有可能无法捕捉到
.finally方法用于指定不管Promise对象最后状态如何,都会执行的操作。它与done方法的最大区别,它接受一个普通的回调函数作为参数,该函数不管怎样都必须执行。
Generator 函数的语法 ***
迭代器或解决异步调用回调地狱
function* gen() {
yield 123 + 456;
}
redux-saga中使用了该语法
async await *
解决异步回调地域问题,一般和promise一起使用
class 语法糖 ;解决了js中没有类的概念,需要使用 function做为构造函数来使用的问题,简化了继承 *
class ColorPoint extends Point {
constructor(x, y, color) {
super(x, y); // 调用父类的constructor(x, y)
this.color = color;
}
toString() {
return this.color + ' ' + super.toString(); // 调用父类的toString()
}
}
模块module *
引入模块 import
输出模块 export
34 typescript *#
- 基础类型: boolean\number\string\数组\元组(元组类型允许表示一个已知元素数量和类型的数组,各元素的类型不必相同)\枚举(enum类型是对JavaScript标准数据类型的一个补充。 像C#等其它语言一样,使用枚举类型可以为一组数值赋予友好的名字)\ Any\ viod\null\undefined\never(类型表示的是那些永不存在的值的类型)\object
- 变量声明:let const var
- 接口 接口的作用就是为这些类型命名和为你的代码或第三方代码定义契约
interface LabelledValue { //接口
label: string;
}
function printLabel(labelledObj: LabelledValue) {
console.log(labelledObj.label);
}
let myObj = {size: 10, label: "Size 10 Object"};
printLabel(myObj);
4.类
class Animal { move(distanceInMeters: number = 0) { console.log(`Animal moved ${distanceInMeters}m.`); }}class Dog extends Animal { bark() { console.log('Woof! Woof!'); }}const dog = new Dog();dog.bark();dog.move(10);dog.bark();
5.函数
function add(x: number, y: number): number { return x + y;}let myAdd = function(x: number, y: number): number { return x + y; };也可以有this和箭头函数
- 泛型可以使用泛型来创建可重用的组件,一个组件可以支持多种类型的数据。 这样用户就可以以自己的数据类型来使用组件。
function identity<T>(arg: T): T { return arg;}let output = identity<string>("myString"); // type of output will be 'string'
- 枚举使用枚举我们可以定义一些带名字的常量。 使用枚举可以清晰地表达意图或创建一组有区别的用例。 TypeScript支持数字的和基于字符串的枚举。
enum Direction { Up = 1, Down, Left, Right}
- 类型推论 这节介绍TypeScript里的类型推论。即,类型是在哪里如何被推断的。
- 类型兼容性 TypeScript里的类型兼容性是基于结构子类型的。 结构类型是一种只使用其成员来描述类型的方式。 它正好与名义(nominal)类型形成对比。
- 高级类型
交叉类型 交叉类型是将多个类型合并为一个类型联合类型 联合类型表示一个值可以是几种类型之一。
- symbol类型的值是通过Symbol构造函数创建的。
let sym1 = Symbol();let sym2 = Symbol("key"); // 可选的字符串key
12.迭代器和生成器
for..of迭代的是对象的 键 的列表for..in则迭代对象的键对应的值
13.模块
export import
14.命名空间 随着更多验证器的加入,我们需要一种手段来组织代码,以便于在记录它们类型的同时还不用担心与其它对象产生命名冲突。 因此,我们把验证器包裹到一个命名空间内,而不是把它们放在全局命名空间下。
- 声明合并
// 合并接口
interface Box {
height: number;
width: number;
}
interface Box {
scale: number;
}
let box: Box = { height: 5, width: 6, scale: 10 };
// 合并命名空间
namespace Animals {
export class Zebra {}
}
namespace Animals {
export interface Legged {
numberOfLegs: number;
}
export class Dog {}
}
-
装饰器
-
三斜线指令
-
文件类型检查
35 队列和栈 *#
队列和栈都是后台语言的数据结构, js可以通过数组的一些方法进行模拟队列和栈
-
栈#
- 定义: 栈是一种后进先出的数据结构,也就是说最新添加(后进)的项最早被移出;它是一种运算受限的线性表,只能在表头/栈顶进行插入和删除操作。栈有栈底和栈顶。
- 入栈和出栈: 向一个栈插入新元素叫入栈(进栈),就是把新元素放入到栈顶的上面,成为新的栈顶;从一个栈删除元素叫出栈,就是把栈顶的元素删除掉,相邻的成为新栈顶;
- js数组方法模拟栈的数据结构
- 入栈push
- 出栈pop
-
队列#
- 定义: 队列是一种先进先出的数据结构。 队列在列表的末端增加项,在首端移除项。
- 出队列和入队列:队列允许在表的首端(队列头)进行删除操作,在表的末端(队列尾)进行插入操作;(例如在银行窗口排队办理业务,最前面的第一个人开始办理,后面来的人只能在队伍末尾排队,直到排到他们为止),队列是实现多任务的重要机制!
- js数组模拟入队列和出队列
- 入队列unshift
- 出队列 pop
36 树组件,遍历树组件 *#
js遍历树形结构方法_河鲜森的博客-CSDN博客_js遍历树形结构数组
37 sass 新特性 **#
Sourcemaps &改善了选择器 改善了if()函数 高级操作列表 新的list-separator()函数 支持Maps 改善了@for循环 @at-root规则 新字符串操作函数 新的@import功能 著作权归作者所有。 商业转载请联系作者获得授权,非商业转载请注明出处。 原文: Sass的新特性_Preprocessor, Sass, SCSS 教程_W3cplus sass和less区别: *
- sass和less主要区别在于实现方式: less是基于JavaScript的在客户端处理 所以安装的时候用npm,sass是基于ruby所以在服务器处理。
- sass与less的安装 sass基于Ruby语言开发而成,因此安装sass前需要安装Ruby。 less 在服务器端最容易的安装方式就是通过 npm(node.js 的包管理器)。less 在客户端使用【.less】(LESS源文件),只需要在官网载一个javascript脚本文件主【less.js】,然后在HTML中引入即可。
- 变量 sass 是以开头定义的变量,如$mainColor: #963; less 是以@开头定义的变量,如 @mainColor: #963;
- 作用域 sass 没有全局变量,满足就近原则,但是实际中可以将需要定义的全局属性放在base.scss 文件中。注意变量名重复; less 中的作用域和其他程序语言中的作用域非常的相同,他首先会查找局部定义的变量,如果没有找到,会像冒泡一样,一级一级往下查找,直到根为止。 区别详细的课参看以下链接: Sass和less的区别是什么?用哪个好 - Tommy_marc - 博客园
38 js排序算法 *#
39 网络攻击,及解决办法 *#
常见的web网络攻击处理方案_weixin_33781606的博客-CSDN博客
40 seo优化 **#
1.合理的title、description、keywords 2.语义化的HTML代码,符合W3C规范:语义化代码让搜索引擎容易理解网页 3.非装饰性图片必须加alt 4.友情链接,好的友情链接可以快速的提高你的网站权重 5.外链,高质量的外链,会给你的网站提高源源不断的权重提升 6.向各大搜索引擎登陆入口提交尚未收录站点 7.重要内容HTML代码放在最前:搜索引擎抓取HTML顺序是从上到下,保证重要内容一定会被抓取 8.少用iframe:搜索引擎不会抓取iframe中的内容 9.提高网站速度:网站速度是搜索引擎排序的一个重要指标
41 数组去重 ***#
JavaScript数组去重(12种方法,史上最全) - SegmentFault 思否
42 倒序打印 *#
43 css横向居中纵向居中#
css居中-水平居中,垂直居中,上下左右居中 - 飘舟 - 博客园CSS水平居中+垂直居中+水平/垂直居中的方法总结_杜媛媛的博客-CSDN博客_居中
44 如何给localStorage设置有效期 **#
https://jingyan.baidu.com/article/b2c186c83eef46c46ef6ffbb.html
45.1 Window. write和document.innerhtml区别 **#
主要区别:document.write是直接将内容写入页面的内容流,会导致页面全部重绘,innerHTML将内容写入某个DOM节点,不会导致页面全部重绘
45.2 display:none和visibility:hidden的区别是: *#
- display:none是彻底消失,不在文档流中占位,浏览器也不会解析该元素;visibility:hidden是视觉上消失了,可以理解为透明度为0的效果,在文档流中占位,浏览器会解析该元素;
- 使用visibility:hidden比display:none性能上要好,display:none切换显示时visibility,页面产生回流(当页面中的一部分元素需要改变规模尺寸、布局、显示隐藏等,页面重新构建,此时就是回流。所有页面第一次加载时需要产生一次回流),而visibility切换是否显示时则不会引起回流。 所以我使用visibility:hidden,在页面渲染时第二个tab页中的轮播图就可以获取宽度做自适应了。
46 Doctype的作用: *#
-
定义:#
DOCTYPE的作用 DOCTYPE是document type (文档类型) 的缩写
-
作用:#
- document.compatMode(文档的解析类型): BackCompat:怪异模式,浏览器使用自己的怪异模式解析渲染页面。 CSS1Compat:标准模式,浏览器使用W3C的标准解析渲染页面。
- Doctype声明了文档的解析类型(document.compatMode),避免浏览器的怪异模式。这个属性会被浏览器识别并使用,但是如果你的页面没有DOCTYPE的声明,那么compatMode默认就是BackCompat,浏览器按照自己的方式解析渲染页面,那么,在不同的浏览器就会显示不同的样式。 如果你的页面添加了那么,那么就等同于开启了标准模式那么浏览器就得老老实实的按照W3C的标准解析渲染页面,这样一来,你的页面在所有的浏览器里显示的就都是一个样子了。这就是Doctype的作用。
47 常用的数组方法 *#
-
join():#
join(separator): 将数组的元素组起一个字符串,以separator为分隔符,省略的话则用默认用逗号为分隔符,该方法只接收一个参数:即分隔符。
-
push()和pop():push():#
可以接收任意数量的参数,把它们逐个添加到数组末尾,并返回修改后数组的长度。 pop():数组末尾移除最后一项,减少数组的 length 值,然后返回移除的项。
-
shift() 和 unshift():shift():#
删除原数组第一项,并返回删除元素的值;如果数组为空则返回undefined 。 unshift:将参数添加到原数组开头,并返回数组的长度
-
sort():#
按升序排列数组项——即最小的值位于最前面,最大的值排在最后面
-
reverse():#
反转数组项的顺序。
-
concat():#
将参数添加到原数组中。这个方法会先创建当前数组一个副本,然后将接收到的参数添加到这个副本的末尾,最后返回新构建的数组。在没有给 concat()方法传递参数的情况下,它只是复制当前数组并返回副本。
-
slice():#
返回从原数组中指定开始下标到结束下标之间的项组成的新数组。slice()方法可以接受一或两个参数,即要返回项的起始和结束位置。在只有一个参数的情况下, slice()方法返回从该参数指定位置开始到当前数组末尾的所有项。如果有两个参数,该方法返回起始和结束位置之间的项——但不包括结束位置的项。
-
splice():splice():#
很强大的数组方法,它有很多种用法,可以实现删除、插入和替换。 删除:可以删除任意数量的项,只需指定 2 个参数:要删除的第一项的位置和要删除的项数。例如, splice(0,2)会删除数组中的前两项。 插入:可以向指定位置插入任意数量的项,只需提供 3 个参数:起始位置、 0(要删除的项数)和要插入的项。例如,splice(2,0,4,6)会从当前数组的位置 2 开始插入4和6。 替换:可以向指定位置插入任意数量的项,且同时删除任意数量的项,只需指定 3 个参数:起始位置、要删除的项数和要插入的任意数量的项。插入的项数不必与删除的项数相等。例如,splice (2,1,4,6)会删除当前数组位置 2 的项,然后再从位置 2 开始插入4和6。 splice()方法始终都会返回一个数组,该数组中包含从原始数组中删除的项,如果没有删除任何项,则返回一个空数组。
-
indexOf()和 lastIndexOf() (ES5新增):#
indexOf():接收两个参数:要查找的项和(可选的)表示查找起点位置的索引。其中, 从数组的开头(位置 0)开始向后查找。 lastIndexOf:接收两个参数:要查找的项和(可选的)表示查找起点位置的索引。其中, 从数组的末尾开始向前查找。 这两个方法都返回要查找的项在数组中的位置,或者在没找到的情况下返回1。在比较第一个参数与数组中的每一项时,会使用全等操作符。
-
forEach() (ES5新增)forEach():#
对数组进行遍历循环,对数组中的每一项运行给定函数。这个方法没有返回值。参数都是function类型,默认有传参,参数分别为:遍历的数组内容;第对应的数组索引,数组本身。
-
map() (ES5新增)map():#
指“映射”,对数组中的每一项运行给定函数,返回每次函数调用的结果组成的数组。
-
filter() (ES5新增)filter():#
“过滤”功能,数组中的每一项运行给定函数,返回满足过滤条件组成的数组。
-
every() (ES5新增)every():#
判断数组中每一项都是否满足条件,只有所有项都满足条件,才会返回true。
-
some() (ES5新增)some():#
判断数组中是否存在满足条件的项,只要有一项满足条件,就会返回true。
-
reduce()和 reduceRight() (ES5新增)#
这两个方法都会实现迭代数组的所有项,然后构建一个最终返回的值。reduce()方法从数组的第一项开始,逐个遍历到最后。而 reduceRight()则从数组的最后一项开始,向前遍历到第一项。 这两个方法都接收两个参数:一个在每一项上调用的函数和(可选的)作为归并基础的初始值。 传给 reduce()和 reduceRight()的函数接收 4 个参数:前一个值、当前值、项的索引和数组对象。这个函数返回的任何值都会作为第一个参数自动传给下一项。第一次迭代发生在数组的第二项上,因此第一个参数是数组的第一项,第二个参数就是数组的第二项。
48 字符串方法 *#
- charAt() 返回指定索引位置的字符
- charCodeAt() 返回指定索引位置字符的 Unicode 值
- concat() 连接两个或多个字符串,返回连接后的字符串
- fromCharCode() 将 Unicode 转换为字符串
- indexOf() 返回字符串中检索指定字符第一次出现的位置
- lastIndexOf() 返回字符串中检索指定字符最后一次出现的位置
- localeCompare() 用本地特定的顺序来比较两个字符串
- match() 找到一个或多个正则表达式的匹配
- replace() 替换与正则表达式匹配的子串
- search() 检索与正则表达式相匹配的值
- slice() 提取字符串的片断,并在新的字符串中返回被提取的部分
- split() 把字符串分割为子字符串数组
- substr() 从起始索引号提取字符串中指定数目的字符
- substring() 提取字符串中两个指定的索引号之间的字符
- toLocaleLowerCase() 根据主机的语言环境把字符串转换为小写,只有几种语言(如土耳其语)具有地方特有的大小写映射
- toLocaleUpperCase() 根据主机的语言环境把字符串转换为大写,只有几种语言(如土耳其语)具有地方特有的大小写映射
- toLowerCase() 把字符串转换为小写
- toString() 返回字符串对象值
- toUpperCase() 把字符串转换为大写
- trim() 移除字符串首尾空白
- valueOf() 返回某个字符串对象的原始值
49 对象方法*#
Object.assign() 对象合并
50 手写防抖、节流,防抖和节流的区别 ***#
概念
在前端开发的过程中,我们经常会需要绑定一些持续触发的事件,如滚动、输入等等,但有些时候我们并不希望在事件持续触发的过程中那么频繁地去执行函数,防抖和节流是比较好的解决方案。
(1)所谓防抖,就是指触发事件后在 n 秒内函数只能执行一次,如果在 n 秒内又触发了事件,则会重新计算函数执行时间。使用场景:注册的时候检查用户名是否已经被注册.
(2)所谓节流,就是指连续触发事件但是在 n 秒中只执行一次函数。节流会稀释函数的执行频率。使用场景:监听滚动条是否到了顶部或底部.
-
防抖例子#
<!DOCTYPE html>
<head>
<meta charset="UTF-8">
</head>
<body> 用户名: <input onblur="checkUsername" placeholder="请输入用户名" id="username" />
<script>
// 1.定义一个timer定时器
var timer;
// 2.获取input节点
var $username = document.getElementById('username');
// 3.绑定input事件
$username.addEventListener('input', function () {
// 4.每次输入都会清除定时器,所以连续输入,定时器是不会触发,触发,除非停止输入超过1秒定时器才会执行
clearTimeout(timer);
timer = setTimeout(function () {
console.log('发请求到后台,检查用户是否已注册');
}, 1000)
}, false)
</script>
</body>
封装函数
<script>
// 上面例子中的函数,一般单独写出来,给它命名为debouncevar
$username = document.getElementById("username");
$username.addEventListener("input", debounce, false);
var timer;
function debounce() {
clearTimeout(timer);
timer = setTimeout(function () {
console.log("发请求到后台,检查用户是否已注册");
}, 1000);
}
</script>
-
节流例子#
<!DOCTYPE html>
<head>
<meta charset="UTF-8">
<style>
p { height: 100px }
</style>
</head>
<body>
<div id="box">
<p>ppppppppppppppppp</p>
<p>ppppppppppppppppp</p>
<p>ppppppppppppppppp</p>
<p>ppppppppppppppppp</p>
<p>ppppppppppppppppp</p>
<p>ppppppppppppppppp</p>
<p>ppppppppppppppppp</p>
<p>ppppppppppppppppp</p>
<p>ppppppppppppppppp</p>
<p>ppppppppppppppppp</p>
<p>ppppppppppppppppp</p>
<p>ppppppppppppppppp</p>
<p>ppppppppppppppppp</p>
<p>ppppppppppppppppp</p>
<p>ppppppppppppppppp</p>
<p>ppppppppppppppppp</p>
<p>ppppppppppppppppp</p>
<p>ppppppppppppppppp</p>
<p>ppppppppppppppppp</p>
<p>ppppppppppppppppp</p>
<p>ppppppppppppppppp</p>
<p>ppppppppppppppppp</p>
<p>ppppppppppppppppp</p>
<p>ppppppppppppppppp</p>
<p>ppppppppppppppppp</p>
<p>ppppppppppppppppp</p>
<p>ppppppppppppppppp</p>
</div>
<script>
var $username = document.getElementById('username');
window.onscroll = throttle; // 1.上一次事件触发的时间
let oldTime = Date.now(); // 10:00:00
function throttle() {
// 现在触发的时间
let newTime = Date.now();
// 现在点击的时间跟上一次点击的时间如果超过了一秒,执行回调
if (newTime - oldTime >= 1000) {
// 在这里执行我们想做的事情
console.log('想干嘛干嘛');
// 从新计时,这次点击的时间成为下一次点击时间的起点
oldTime = newTime;
}
}
</script>
</body>
-
防抖和节流的区别#
函数防抖和节流区别在于,当事件持续被触发,如果触发时间间隔短于规定的等待时间(n秒),那么
- 函数防抖的情况下,函数将一直推迟执行,造成不会被执行的效果;
- 函数节流的情况下,函数将每个 n 秒执行一次。
https://www.jb51.net/article/105601.htm什么是防抖和节流?有什么区别?如何实现?_~倾半世阳光的博客-CSDN博客_防抖和节流的区别
51 浏览器渲染原理 ,回流,重绘 ***#
(1) 浏览器的渲染机制#
浏览器的渲染机制一般分为以下几个步骤:
- 处理 HTML 并构建 DOM 树。
- 处理 CSS 构建 CSSOM 树。
- 将 DOM 与 CSSOM 合并成一个渲染树。
- 根据渲染树来布局,计算每个节点的位置。
- 调用 GPU 绘制,合成图层,显示在屏幕上。
(2) 重绘与回流#
重绘
当元素样式的改变不影响布局时,浏览器将使用重绘对元素进行更新,此时由于只需要UI层面的重新像素绘制,因此 损耗较少
回流
当元素的尺寸、结构或触发某些属性时,浏览器会重新渲染页面,称为回流。此时,浏览器需要重新经过计算,计算后还需要重新页面布局,因此是较重的操作。会触发回流的操作:
– 页面初次渲染 – 浏览器窗口大小改变 – 元素尺寸、位置、内容发生改变 – 元素字体大小变化 – 添加或者删除可见的 dom 元素 – 激活 CSS 伪类(例如::hover) – 查询某些属性或调用某些方法: – clientWidth、clientHeight、clientTop、clientLeft – offsetWidth、offsetHeight、offsetTop、offsetLeft – scrollWidth、scrollHeight、scrollTop、scrollLeft – getComputedStyle() – getBoundingClientRect() – scrollTo()
总结:
回流必定触发重绘,重绘不一定触发回流。重绘的开销较小,回流的代价较高。所以尽量减少触发回流的操作, 以达到更好的性能
52 css3选择器优先级及计算 ***#
- !important(在样式属性后不加“;”,在!important后加分号)>行内样式>ID选择器>Class选择器>标签>通配符 相同级别:
- 同一级别中后写的会覆盖先写的样式
- CSS优先级:是由四个级别和各级别出现次数决定的 四个级别:行内样式,ID选择器,Class选择器,标签 每个规则对应一个初始值0,0,0,0 若是 行内选择符,则加1、0、0、0 若是 ID选择符,则加0、1、0、0 若是 类选择符/属性选择符/伪类选择符,则分别加0、0、1、0 若是 元素选择符/伪元素选择符,则分别加0、0、0、1 算法:将每条规则中,选择符对应的数相加后得到的”四位数“,从左到右进行比较,大的优先级越高。 注意: ①、!important的优先级是最高的,但出现冲突时则需比较”四位数“; ②、优先级相同时,则采用就近原则,选择最后出现的样式; ③、继承得来的属性,其优先级最低; !important > 行内样式>ID选择器 > 类选择器 > 标签 > 通配符 > 继承 > 浏览器默认属性 css选择器使用强烈建议采用低权重原则,利于充分发挥css的继承性,复用性,模块化、组件化。
53 移动端的1px线虚掉怎么解决*#
移动端1px细线解决方案总结 - lunarorbitx - 博客园
54 点击事件在移动端会有300毫秒延迟,为什么,怎么解决 *#
<!DOCTYPE html>
<html lang="en">
<body>
<div id="targetDiv" style="width: 200px; height: 200px; background-color: gray"></div>
<script>
const Div = document.getElementById("targetDiv");
Div.addEventListener("touchstart", function () {
console.log("触发了touchstart事件");
});
Div.addEventListener("click", (e) => {
console.log("触发了click事件");
});
</script>
</body>
</html>
-
300ms延迟的产生缘由#
移动端浏览器的默认显示宽度是980px(不同机型各异,但相差不大),而不是屏幕的宽度(320px或其他)。为了对早期普通网页更好的体验,iphone设计了双击放大显示的功能–这就是300ms延迟的来源:如果用户一次点击后300ms内没有其他操作,则认为是个单击行为;否则为双击放大行为
-
解决方法#
-
设置不能缩放:user-scalable=no。 不能缩放就不会有双击缩放操作,因此click事件也就没了300ms延迟,这个是Chrome首先在Android中提出的。
-
设置显示宽度:width=device-width。Chrome 开发团队不久前宣布,在 Chrome 32 这一版中,他们将在包含 width=device-width 或者置为比 viewport 值更小的页面上禁用双击缩放。当然,没有双击缩放就没有 300毫秒点击延迟。
-
IE的指针事件 (Pointer Events):设置touch-action:none,根据规范,touch-action 属性决定 “是否触摸操作会触发用户代理的默认行为。这包括但不限于双指缩放等行为”。从实际应用的角度来看,touch-action决定了用户在点击了目标元素之后,是否能够进行双指缩放或者双击缩放。因此,这也相当完美地解决了300 毫秒点击延迟的问题 鉴于上述的3种解决方案,现在较为通用的meta设置为:
"width=device-width,initial-scale=1,maximum-scale=1,user-scalable=no"
-
使用插件 fastclick(react项目中使用的就是fastclick插件)
1. 安装 npm i fastclick --save 2. 全局引入 import fastclick from 'fastclick' 3. 配置 fastClick.attach(document.body);
55 移动端小刘海怎么解决 *#
iPhone X Web前端页面适配(处理可爱的刘海)_简单的阿贵的博客-CSDN博客 移动: 1.onclick事件的点击穿透 使用 ontap替换 2.移动端点击事件300ms延时 在头部的viewport 设置禁止缩放 3.iphone手机的小刘海,iphone手机的home键
iphoneX移动端适配问题_C. Two的博客-CSDN博客_ios移动端适配兼容iphone x * 刘海的正确姿势 - 腾讯Web前端 IMWeb 团队社区 | blog | 团队博客
兼容iphone x * 刘海的正确姿势 - 腾讯Web前端 IMWeb 团队社区 | blog | 团队博客
新增#
- javascript的 typeof 返回哪些数据类型
- 前段缓存有哪些方法
- 程序概念
- 递归是什么
- 什么是链式调用
- 什么是观察者模式与发布订阅模式
- 编程语言概念中的类怎么设计, 请简单举例
- vue或者angular中的路由跳转, 如何用原生js实现, 请描述你的实现思路
- 简述以下什么是时间代理事件委托
- 写一个简单的闭包函数, 闭包有哪些优点?
- 列举几种判断一个变量是否是数组的方法? 都分别有什么问题或特点?
- 列举几种数组去重的方法? 都分别有什么问题或特点
- 列出几种常见的拷贝方法? 都分别有什么问题或特点