一 JS中的原型和原型链的理解?
// 补充 判断属性和方法是否是本身的:xx.hasOwnProperty('属性名'|| 方法名)
二 闭包(在防抖节流中使用了)
闭包的缺点
变量会驻留在内存中,造成内存损耗问题。
解决:把闭包的函数设置为null
二 防抖节流(补充)
项目中:用户名密码登录使用了,uview提供好的。
防抖:(只执行最后一次)用户触发事件过于频繁,只要最后一次事件的操作,比如:实时搜索,拖拽
防抖代码:
<input type="text">
let inp = document.querySelector('input')
inp.oninput = debounce(function () {
console.log(this.value);
}, 500)
function debounce(fn, delay) {
let t = null;
return function () {
if (t !== null) {
clearTimeout(t)
}
t = setTimeout(() => {
fn.call(this);
}, delay);
}
}
节流:(控制执行次数) 短时间内大量执行事件,就只执行一次(比如页面滚动)
window.onscroll = throttle(function () {
console.log("hello");
}, 500)
function throttle(fn, delay) {
let flag = true;
return function () {
if (flag) {
setTimeout(() => {
fn.call(this)
flag = true
}, delay);
}
flag = false
}
}
防抖:在搜索框 会监听搜索内容的变化,有变化就发送请求,这样比较耗费资源,
使用第三方工具包 lodash 的 debounce 方法,在设定的防抖时间内输入内容,不需要发送请求,直到停下的时候,
超过设定的时间到了才发送请求,没停下之前,一直输入是不会发送请求的
应用场景:实时搜索,拖拽
完整的写法要写上handler 才能配置 immediate: true 和 deep:true
// debounce 函数
// 参数1:函数
// 参数2:防抖时间
// 返回值:防抖之后的函数,和参数1功能是一样的
toutiao-m: views search search-suggess.vue
// 子组件监听父组件搜索框内容的变化
// lodash 支持按需加载,有利于打包结果优化 1、安装 lodash
import { debounce } from "lodash"
watch: {
searchText: {
// handler(value) {
// console.log(value)
// this.loadSearchSuggess(value) // 调用搜索请求
// },
handler: debounce(function (value) { // debounce 是防抖优化的功能 1、安装 lodash
// console.log(value)
this.loadSearchSuggess(value)
}, 200),
immediate: true // 该回调将会在侦听开始之后被立即调用
}
},
二(补充) 闭包 和 高阶函数有什么区别?
**高阶函数**他**接收函数作为参数**或者**将函数作为返回值输出**, 是对其他函数进行操作的函数,
高级函数使用场景:数组的很多方法都是
sort some every find findIndex filter
promise
ajax回调函数
var getUserInfo = function( userid, callback) {
$.ajax('http://xxx.com/getUserInfo?' + userid, function( data ){
if (typeof callback === 'function') {
callback( data )
}
});
}
getUserInfo(1233,function( data ){
console.log( data )
});
数据类型检测:
var isType = function( type ){
return function( obj ){
return Object.prototype.toString.call( obj ) === '[object ' + type +']';
}
}
var isString = isType( 'String' );
var isArray = isType( 'Array' );
var isNumer = isType( 'Number' );
console.log( isArray([1,2,3]) );
创建100个div之后 一次性隐藏
var appendDiv = function( callback ){
for (var i = 0; i < 100; i++){
var div = document.createElement('div');
div.innerHTML = i;
document.body.appendChild( div );
if (typeof callback === 'function'){
callback( div )
}
}
}
appendDiv( function( node ){
node.style.display = 'none'
});
三 ES6新增特性?
1 let/const, var区别:
1.1 var声明变量存在变量提升,let和const不存在变量提升
1.2 let、const都是块级局部变量 就是只在当前代码块起作用
1.3 const 声明时候必须赋值 ,let 只能进行一次赋值,
const 声明后不能再修改 ,如果声明的是复合类型数据,可以修改其属性
同一作用域下
1.4 let和const不能声明同名变量,而var可以
2 箭头函数 和 普通函数的区别:
2.1 普通函数可以有匿名函数,也可以有具体名函数,但是箭头函数都是匿名函数。
2.2 箭头函数不能用于构造函数,因为创建构造函数的过程中,会将this指向创建的对象,但是箭头函数本身没有this,所以会报错。
2.3 箭头函数中this的指向不同
在普通函数中 this总是指向调用它的对象,如果用作构造函数,this指向创建的对象实例。
箭头函数本身没有this,但是它在声明时可以捕获其外层执行执行环境的this,this一旦被捕获,就不再发生变化,任何方法都改变不了其指向,如 call() , bind() , apply()
箭头函数在全局作用域声明 所以它捕获全局作用域中的this,this指向window对象。
2.4 箭头函数不绑定arguments,取而代之用rest参数…解决
2.5 箭头函数不具有prototype原型对象。
2.6 箭头函数不具有super。
2.7 箭头函数不具有new.target。
2.8 箭头函数不能Generator函数,不能使用yeild表达式关键字。
3 模板字符串
4 解构赋值,
5 模块的导入(import)和导出(export default/export),
6 Promise 用于更优雅地处理异步请求。
Promise是异步编程的一种解决方案,有了它就可以避免层层嵌套的回调函数。Promise是一个容器,
通常保存着一个异步操作的结果。
1.生成Promise实例。例:
const promise = new Promise(function(resolve, reject) {
// ... some code
if (/* 异步操作成功 */){
resolve(value);
} else {
reject(error);
}});
其中resolve和reject是两个由js引擎提供的函数,分别用于将状态从“进行中”变为“成功”或“失败”,
调用它们并不会终结Promise的参数函数的执行。
2.待实例生成后,用then分别指定两种状态的回调函数:
promise.then(function(value) {
// success
}, function(error) {
// failure
});
3.还可以用catch()指定发生错误时的回调函数,用finally()指定不管Promise对象最后状态如何都会执行的操作。例:
promise
.then(result => {···})
.catch(error => {···})
.finally(() => {···});
4.Promise.all()用于将多个Promise实例包装成一个新的Promise实例,多个请求会并发执行,只有每个状态都变成fulfilled(成功)才会变成fulfilled,只要有一个rejected(失败)则合起来状态变成rejected。例:
const p = Promise.all([p1, p2, p3]);
7 async/await 区别
async特点:是用来定义异步函数的,直接打印函数名会得到一个promise对象,就可以使用函数名称去调用.then方法,其实就是promise对象在调用这个方法了。
await特点:await后面跟的是任意表达式,但是一般使用的时候,一般放的是promise的表达式。
async 内部实现,如果执行成功了,有返回值,其实执行的是 promise.resolve() 如果出错的话,调用的是 promise.reject(), 最后都会返回promise对象,用catch捕获到。
await绑定的事promise对象,等待后面的promise对象执行完毕,拿到 promise.resolve()返回值,再往下继续执行后面的代码,所有有一个等待的意思,如果Promise对象变为reject状态,整个async函数都会中断执行,因此可以将await放在try...catch里面。
优势:es7的语法, 比promise更好的解决了回调地狱,使得异步操作更加方便,async函数完全可以看作多个异步操作包装成的一个Promise 对象
补充: promise async await的区别:
promise 是 es6的,链式操作
promise 中有自己的捕获机制,包含 catch ,因为我们知道 如果reject或resolve,如果出了错,他会自己报错的
而 async 或 await 的话主要是在函数内自己定义才能正常使用
promise 提供的方法比async多一些,比如常见的 all方法,race方法,
Promse.race就是赛跑的意思,)里面哪个结果获得的快,就返回那个结果,不管结果本身是成功状态还是失败状态
这就是他们的一些区别。
async await比较好用,这是将来一定的主流,但是promise可以达到同样的效果,这是对 promise async 和 await区别的了解。
以前的答案:
ES2017引入async使得异步操作更加方便,async函数完全可以看作多个异步操作包装成的一个Promise 对象。
async表示函数里有异步操作,await表示后面的表达式需要等待结果。例:
async function getStockPriceByName(name) {
const symbol = await getStockSymbol(name);
const stockPrice = await getStockPrice(symbol);
return stockPrice;
}
getStockPriceByName('goog').then(
function (result) {console.log(result);});
async函数返回一个Promise对象,因此可以使用then添加回调函数,async内部返回的值会成为then的参数。该Promise 对象必须等到内部所有await命令后面的Promise对象执行完,才会发生状态改变,除非遇到return语句或者抛出错误。也就是说,只有async函数内部的异步操作执行完,才会执行then方法指定的回调函数。
函数执行时一旦遇到await就会先返回,等异步操作完成再接着执行函数体内后面的语句。如果多个异步操作不存在继发关系,最好用Promise.all()让它们同时触发,缩短程序执行时间。
任何一个await语句后面的Promise对象变为reject状态,整个async函数都会中断执行,因此可以将await放在try...catch里面。
8 for of遍历的是键值对中的值
9 for in遍历的是键值对中的键
10 Symbol 一种新的原始数据类型Symbol,表示独一无二的值
Symbol通过Symbol函数生成,可接受一个字符串作为参数,该字符串只是作为对当前Symbol值的描述,因此相同参数的Symbol函数的返回值并不相等。例:
let s = Symbol('foo');
s.description //'foo'
11 Set与Map
Set 存储任何类型的唯一值,即集合中所保存的元素是不重复的。类数组结构。
Map 是一组键值对的结构,具有极快的查找速度
const set = new Set();
const map = new Map();
Set的属性和方法有:
size属性返回结构成员总数,
add()添加某个值,
delete()删除某个值,
has()查找某值是否为其成员,
clear()清除所有成员;
Map的属性和方法有:
size、
set(key, value)、
get(key)、
delete(key)、
has(key)、
clear()。
与数组一样Set和Map都有forEach()使用回调函数遍历每个成员,参数依次为键值、键名(set的键名就是键值),例:
mySet.forEach((value, key) => console.log(key + ' : ' + value));
另外,扩展运算符...也可以用于Set和Map,例:let arr = [...set];
12 还有一些数组字符串的新方法
Array.of() 可以解决array参数不一样的时候 返回结果的问题。
Array.of(1,2,3) // [1,2,3]
Array.of(3) // [3]
Array(3) // [,,,]
Array.from()
1.会把一个类数组或对象,转成真正的数组,但是这个类数组必须具有length属性
var list={
0:"韩信",
1:"李白",
2:"诸葛亮",
3:"赵云",
length:4
}
var arr = Array.from(list)
console.log(arr);//输出结果: ["韩信", "李白", "诸葛亮", "赵云"]
2.可以讲一个字符串转成数组的形式
var str = 'chenbin'
var arr1 = Array.from(str)
console.log(arr1);// 输出结果: ["c", "h", "e", "n", "b", "i", "n"]
3.可以传入第二个参数,类似于map()方法(Array.from(obj,mapFn,thisArg)的格式相当于Array.from(obj).map(mapFn,thisArg)),将每个元素进行处理,将处理后的元素放回数组
var arr3 = [1,2,3,4,5]
var newarr = new Set(arr3)
console.log(Array.from(newarr,item=>item+1));//输出结果: [2, 3, 4, 5, 6]
forEach() 方法用于调用数组的每个元素,并将元素传递给回调函数,对于空数组是不会执行回调函数的。
let arrInfo=[4,6,6,8,5,7,87]
arrInfo.forEach((item,index,arr)=>{
//遍历逻辑
})
其中:
item代码遍历的每一项,
index:代表遍历的每项的索引,
arr代表数组本身
filter() 方法创建一个新的数组,新数组中的元素是通过检查指定数组中符合条件的所有元素。
array.filter(function(currentValue,index,arr), thisValue)
注意: filter() 不会对空数组进行检测。
注意: filter() 不会改变原始数组。
every() 方法用于检测数组所有元素是否都符合指定条件(通过函数提供)。
every() 方法使用指定函数检测数组中的所有元素:
如果数组中检测到有一个元素不满足,则整个表达式返回 *false* ,且剩余的元素不会再进行检测。
如果所有元素都满足条件,则返回 true。
array.every(function(currentValue,index,arr), thisValue)
注意: every() 不会对空数组进行检测。
注意: every() 不会改变原始数组。
some() 方法用于检测数组中的元素是否满足指定条件(函数提供)。
some() 方法会依次执行数组的每个元素:
如果有一个元素满足条件,则表达式返回true , 剩余的元素不会再执行检测。
如果没有满足条件的元素,则返回false。
array.some(function(currentValue,index,arr),thisValue)
注意: some() 不会对空数组进行检测。
注意: some() 不会改变原始数组。
find()查找数组中符合条件的元素,若有多个符合条件的元素,则返回第一个元素。
let arr = Array.of(1, 2, 3, 4);
console.log(arr.find(item => item > 2)); // 3
// 数组空位处理为 undefined
console.log([, 1].find(n => true)); // undefined
findIndex()查找数组中符合条件的元素索引,若有多个符合条件的元素,则返回第一个元素索引。
let arr = Array.of(1, 2, 1, 3);
// 参数1:回调函数
// 参数2(可选):指定回调函数中的 this 值
console.log(arr.findIndex((item) => item == 2)); // 1
// 数组空位处理为 undefined
console.log([, 1].findIndex((n) => true)); //0
fill() 一定范围索引的数组元素内容填充为单个指定的值。
let arr = Array.of(1, 2, 3, 4);
// 参数1:用来填充的值
// 参数2:被填充的起始索引
// 参数3(可选):被填充的结束索引,默认为数组末尾
console.log(arr.fill(0,1,2)); // [1, 0, 3, 4]
includes() 数组是否包含指定值。如果找到匹配的字符串则返回 true,否则返回 false,includes() 方法区分大小写
注意:与 Set 和 Map 的 has 方法区分;Set 的 has 方法用于查找值;Map 的 has 方法用于查找键名。
13 对象新方法:
Object.assign(target, source_1, ···)
let target = {a: 1};
let object2 = {b: 2};
let object3 = {c: 3};
Object.assign(target,object2,object3);
// 第一个参数是目标对象,后面的参数是源对象
target; // {a: 1, b: 2, c: 3}
如果目标对象和源对象有同名属性,或者多个源对象有同名属性,则后面的属性会覆盖前面的属性。
如果该函数只有一个参数,当参数为对象时,直接返回该对象;当参数不是对象时,会先将参数转为对象然后返回。
Object.assign(3); // Number {3}
typeof Object.assign(3); // "object"
注意:因为 null 和 undefined 不能转化为对象,所以会报错:
Object.assign(null); // TypeError: Cannot convert undefined or null to object
Object.assign(undefined); // TypeError: Cannot convert undefined or null to object
当参数不止一个时,null 和 undefined 不放第一个,即不为目标对象时,会跳过 null 和 undefined ,不报错
Object.assign(1,undefined); // Number {1} Object.assign({a: 1},null); // {a: 1}
Object.assign(undefined,{a: 1}); // TypeError: Cannot convert undefined or null to object
**注意点**
assign 的属性拷贝是浅拷贝:
let sourceObj = { a: { b: 1}};
let targetObj = {c: 3};
Object.assign(targetObj, sourceObj);
targetObj.a.b = 2;
sourceObj.a.b; // 2
同名属性替换
targetObj = { a: { b: 1, c:2}};
sourceObj = { a: { b: "hh"}};
Object.assign(targetObj, sourceObj);
targetObj; // {a: {b: "hh"}}
数组的处理
Object.assign([2,3], [5]); // [5,3]
会将数组处理成对象,所以先将 [2,3] 转为 {0:2,1:3} ,然后再进行属性复制,
所以源对象的 0 号属性覆盖了目标对象的 0
四:数组的方法有哪些?哪些改变原数组?
splice sort push pop unshift shift slice concat filter map some every join,forEach,
变的有:splice sort push pop unshift shift
splice(index,n,item..) 会改变原始数组, 从第几个开始删除,删除多少个,第三个是可选参数,向数组添加元素
sort(fn(a,b)=>{return a-b}) 数组排序 这种方法会改变原始数组!
push,pop ,unshift,shift改变数组的长度
不变有:6 slice concat filter map some every
slice(start,end) 不会修改数组,从第几个开始删,删多少个,返回一个新数组
concat() 不会改变现有的数组,返回一个新组数。展开运算符...也是这样
filter() 不会改变原始数组,方法创建一个新的数组,新数组中的元素是通过检查指定数组中符合条件的所有元素。
map() 不会改变原始数组,数组中的元素为原始数组元素调用函数处理后的值。
some() 不会改变原始数组,如果有一个元素满足条件,则表达式返回true, 没有满足条件的元素,则返回false。
every() 不会改变原始数组,组中检测到有一个元素不满足,则整个表达式返回 false ,且剩余的元素不会再进行检测
五 for 和 foreach 和 map 有什么区别
1、中止循环:
for 通过 break 关键字来中止循环,
forEach 和 map 不可以,用 try catch/every/some 代替。
2、跳过此次循环:
for 通过 continue 来跳过,
forEach 通过 return 跳过,
map 不能跳过
3、返回值
map 返回一个数组,在 map 的回调函数中,不使用 return 返回值的话,会返回 undeifned。
for 和 forEach 没有返回值。
4、改变原数组。
map 不改变原数组,for 和 forEach 可以改变原数组。
六 for in 和 for of 区别 他们都可以使用 它可以正确响应break、continue和return语句
for in 通常用来遍历对象
for in 遍历数组的毛病
index索引为字符串型数字,不能直接进行几何运算
会遍历数组所有的可枚举属性,包括原型
for of遍历的只是数组内的元素,而不包括数组的原型属性和索引,
七 说一下call,apply,bind区别 可以改变函数中的this指向:call apply bind
新答案:
共同点:功能一致,可以改变this指向
区别:
1. call、apply可以立即执行。bind不会立即执行,因为bind返回的是一个函数需要加入()执行。
2. 参数不同:apply第二个参数是数组。call和bind有多个参数需要挨个写,逗号隔开。
函数.call(调用函数时内部this具有值-也就是调用的函数, 参数1,参数2)
函数.apply(调用函数时内部this具有值-也就是调用的函数, [参数1,参数2])
var arr1 = [1,1112,4,1112,5,7,3,321];
console.log( Math.max.apply(null,arr1) )
函数.bind(调用函数时内部this具有值-也就是调用的函数, 参数1,参数2)()
八 说一下作用域和作用域链的理解?
答:JS作用域也就是JS识别变量的范围,作用域链也就是JS查找变量的顺序
先说作用域,JS作用域主要包括全局作用域、局部作用域和ES6的块级作用域
全局作用域:也就是定义在window下的变量范围,在任何地方都可以访问,
局部作用域:是只在函数内部定义的变量范围
块级作用域:简单来说用let和const在任意的代码块中定义的变量都认为是块级作用域中的变量,例如在for循环中用let定义的变量,在if语句中用let定义的变量等等
注:尽量不要使用全局变量,因为容易导致全局的污染,命名冲突,对bug查找不利。
2️. 而所谓的作用域链就是由最内部的作用域往最外部,查找变量的过程.形成的链条就是作用域链
九 深拷贝 和 浅拷贝 juejin.cn/editor/draf…
新答案:
共同点都是:复制
浅拷贝:只复制引用,而未复制真正的值。
比如声明一个变量
var a = {
a: 1
}
var b = a
console.log(a, b);
a.a = 2
console.log(a, b);
变量声明的变量a的内存块,指向一个内存空间,
变量b是的值是a,所以也跟a一样指向同一个内存空间
所以浅拷贝就是 这种直接赋值的.
可以用以下方式来实现:
用es6的Object.assign({},{})进行对象合并,一般用于数据类型比较简单,层数不大于1的数据
如果是数组可以用es6的Array.from,
或是es6的扩展运算符...arr, 如果使用es5需要用循环来做浅拷贝,
还有一种就是:Object.assign()
var a = {
a: 1
}
let b = Object.assign(a);
a.a = 2
console.log(a, b);
深拷贝:是复制真正的值 ,但使用的是不同引用
深拷贝的方法有:
1. JSON.parse(JSON.stringify(对象))的方式实现深拷贝,有些类型的值是不支持的(undefined , function, RegExp)
var a = {
a: 1
}
var b = JSON.parse(JSON.stringify(a))
console.log(a, b);
a.a = 2
console.log(a, b);
2. 使用递归的形式来封装函数
旧答案:
浅拷贝:
原对象
const oldObj = {
name: "张三",
age:20,
colors:['orange','green','blue'],
friend:{
}
}
const newObj = oldObj
newObj.name = "lisi"
console.log(newObj); // {name: "lisi"}
console.log(oldObj); // {name: "lisi"}
用es6的Object.assign({},{})进行对象合并,如果是数组可以用es6的Array.from,
或是es6的扩展运算符...arr, 如果使用es5需要用循环来做浅拷贝,
深拷贝: 可以用递归的形式来实现.
// 原对象
const oldObj = {
name: "张三",
age: 20,
colors: ['orange', 'green', 'blue'],
friend: {
name: '小明'
}
}
// 定义一个深拷贝函数deepClone
function deepClone(obj = {}) {
// 判断要拷贝的数据类型 是不是对象 ,如果是对象 直接返回
if (typeof obj !== "object" || obj == null) {
return obj
}
let result;
// 判断 要拷贝的数据是否为数组 如果是数组 默认等于一个空数组
if (obj instanceof Array) {
result = [];
} else {
result = {}
}
// for in 循环拷贝的对象 并再次调用自己deepClone的函数
for (let key in obj) {
result[key] = deepClone(obj[key] ) // result[name] = obj[name] ==> result = {name: '张三'}
// 这里在调用一次这个方法就是递归
}
return result;
}
const newObj = deepClone(oldObj)
newObj.name = "lisi"
newObj.friend.name = 80
newObj.colors[0] = 80
console.log(oldObj,"oldObj 原始值");
console.log(newObj,"newObj 新的值");
当然也可以使用JSON.parse(JSON.stringify(对象))的方式实现深拷贝,有些类型的值是不支持的(有缺陷)
十 js变量类型:
原始数据类型(栈):string number boolean undefiend null symbol(ES6新增)
引用类型(栈 和 堆):object(包括function、array、Date...)
typeof 检查 对象 数组 null 都是 object
[] instanceof Array // true
[] instanceof Object // true
##### 一、数据类型:
1、基本数据类型:String、Number、Boolean、Null、Undefined、Symbol 、BigInt
2、引用数据类型:Object、Array、Function、Date、RegExp
二、检测数据类型的四种方法
1、typeof:
(1)检测方法:变量 typeof 数据类型(a typeof string)
(2)总结:只能准确检测:string、number、boolean、undefined、symbol、function,弊端是其他(object、array、date、regExp、null)都会返回object;
2、instanceof:
(1)检测方法:对象 instanceof 引用数据类型(obj instanceof Object)
(2)总结:instanceof的本质作用是判断某个对象是由哪个类(构造函数)产生的,所以只能用在引用数据类型上,如果在原型上找到返回true,所以用它可以区分是引用类型还是基本类型数据;不能判断null,undefined
3、constructor:
(1)检测方法:数据类型.constructor 数据类型([].constructor == Array)
(2)总结:不能判断null,undefined,其它的都可以,由于类的constructor可以随意更改,此时会存在判断不准确的问题
4、Object.prototype.toString.call():
(1)检测方法:Object.prototype.toString.call(数据类型)
(2)总结:该方法是最准备的检测数据类型的方法。由于Object.prototype.toString()本身允许被修改,所以需要调用Object.prototype.toString.call(arg)来判断arg的类型,call将arg的上下文指向Object,所以arg执行了Object的toString方法。
十 js事件循环
事件执行顺序:
1. 同步代码for循环,主线程的运行战中先执行
2. nextTick -->
3. 异步任务,在任务队列中执行
微任务:promise.then
宏任务:settimeOut,ajax,读取文件
4. setImmediate(当前事件循环结束,然后执行)
十一 setTimeout/setInterval 区别
setInterval()会不停的调用函数
setTimeout()只执行函数一次
setTimeout()方法只运行一次,也就是说当达到设定的时间后就出发运行指定的代码,运行完后就结束了,
如果还想再次执行同样的函数,可以在函数体内再次调用setTimeout(),可以达到循环调用的效果。
setInterval()是循环执行的,即每达到指定的时间间隔就执行相应的函数或者表达式,是真正的定时器。
十二 说一下从输入URL到页面加载完中间发生了什么?
答:大致过程是这样的:
1. DNS解析
2. TCP连接
3. 发送HTTP请求
4. 服务器处理请求并返回需要的数据
5. 浏览器解析渲染页面
6. 连接结束
输入了一个域名,域名要通过DNS解析找到这个域名对应的服务器地址(ip),通过TCP请求链接服务,
通过WEB服务器(apache)返回数据,浏览器根据返回数据构建DOM树,通过css渲染引擎及js解析引擎将页面渲染出来,
关闭tcp连接
十三 JS继承:待补充
1.class继承
优点:语法简单易懂,操作更方便。
缺点:并不是所有的浏览器都支持class关键字
2.原型链继承
让子类构造函数的prototype属性指向其父类构造函数的实例;
优点:简单易实现
缺点:不能传递参数,因为对象是一次性创建的。
3.借用构造函数继承
在子类构造函数中使用call,apply调用父类构造函数并改变this指向子类;
优点:子类构建时可以向父类传参
缺点:无法复用父类原型属性和方法
由于构造函数每次执行都重新运行私有方法,所以各个实例私有属性相互独立。
4.组合继承
优点:既可以向父类传参,又可以共享父类原型方法与属性
缺点:调用两次构造函数
十四 JS设计模式有哪些?
答:JS设计模式有很多,但我知道的有单例模式,观察者模式
单例模式:就是保证一个类只有一个实例,实现的方法一般是先判断实例存在与否,如果存在直接返回,
如果不存在就创建了再返回,这就确保了一个类只有一个实例对象。在JavaScript里,单例作为一个命名空间提供者,
从全局命名空间里提供一个唯一的访问点来访问该对象。
观察者模式: 观察者的使用场合就是:当一个对象的改变需要同时改变其它对象,
并且它不知道具体有多少对象需要改变的时候,就应该考虑使用观察者模式。
总的来说,观察者模式所做的工作就是在解耦,让耦合的双方都依赖于抽象,而不是依赖于具体。
从而使得各自的变化都不会影响到另一边的变化
十五 说一下JS事件代理(也称事件委托)是什么,及实现原理?
答:JS事件代理就是通过给父级元素(例如:ul)绑定事件,不给子级元素(例如:li)绑定事件,
然后当点击子级元素时,通过事件冒泡机制在其绑定的父元素上触发事件处理函数,
主要目的是为了提升性能,因为我不用给每个子级元素绑定事件,只给父级元素绑定一次就好了,
在原生js里面是通过event对象的targe属性实现
十六 事件冒泡 和 事件默认行为
阻止冒泡事件:
e.stopPropagation();
cancelBubble = ture
阻止默认事件: e.preventDefault();
十七 cookie,localStorage和sessionStorage的区别
新答案:
公共点:在客户端存放数据
区别:
1. 数据存放有效期
sessionStorage : 仅在当前浏览器窗口关闭之前有效。【关闭浏览器就没了】
localStorage : 始终有效,窗口或者浏览器关闭也一直保存,所以叫持久化存储。
cookie : 只在设置的cookie过期时间之前有效,即使窗口或者浏览器关闭也有效。
2. localStorage、sessionStorage不可以设置过期时间
cookie 有过期时间,可以设置过期(把时间调整到之前的时间,就过期了)
3. cookie需要环境支持才可以运行
localStorage、sessionStorage 不需要环境
3. 存储大小的限制
cookie存储量不能超过4k
localStorage、sessionStorage不能超过5M
根据不同的浏览器存储的大小是不同的。
使用场景:
在项目中token存储是比较多的,token都在本地存储中,
token过期时间2种使用方式:
localStorage + token ==> 后端判断 给前端返回cood码
cookie + token ==》后端判断是否过期,修改过期时间(前后端不分离可以采用这个,前端直接处理,不需要请求,直接判断)
旧答案:
补充 浏览器缓存
### 强制缓存
不会向服务器发起请求,会看缓存中是否有未过期的缓存,当浏览器去请求某个资源时,
通过服务器设置响应头字段[cache-control]或expires来设置缓存的时间、缓存类型。
当缓存中有未过期的缓存,读取缓存并返回statecode200状态。
cache-control(HTTP1.1)
常用值设置:
max-age(缓存时间)、
public(客户端和代理服务器都可以缓存该资源)/private(仅客户端可以缓存该资源)、
immutable(在有效期内直接读取缓存)、
no-cache(跳过强缓存,直接进入协商缓存)/no-store(跳过强缓存和协商缓存)
expires(HTTP1.0)
可以通过expires指定资源的过期时间。是一个绝对时间,由服务器决定。
1.3 cache-control和expires优先级
当cache-control和expires发生冲突时,cache-control的优先级更高。
原因有两点,如下:
1.expires通过具体的时间戳定义缓存的过期时间,由服务器决定。
但是客户端时间可以修改,当客户端和服务端时间不一致的时候会出现问题。
2.当cache-control中设置了“max-age”这类时,expires头会被忽略。
### 协商缓存
当强缓存失效或者被跳过时,进入协商缓存,协商缓存需要和服务器进行交互。
在客户端第一次向服务器发起请求时,通过服务器设置响应头etag(HTTP1.1)和
last-modified(HTTP1.0)来指明资源标识和文件修改的时间。
当浏览器再向服务器发起请求时,请求头会携带If-None-Match(对应etag)和
If-Modified-Since(对应last-modified)来匹配资源和记录上次资源修改的时间
(即上个响应头中的last-modified)。
当匹配成功时,statecode为304,不会返回资源,客户端读取缓存。
当匹配失败时,statecode为200,返回资源。
十九 new操作符具体做了什么?
研究一下 函数
研究一下对象: