1. 原生js基础?
2. js的数据类型有几种?
答:8种:Number,String,Boolean,Null,Undefined,Object,Symbel,Bigint
3. 闭包及实际案例?
答:变量的作用域无非就是两种:全局变量和局部变量。
"链式作用域"结构(chain scope),子对象会一级一级地向上寻找所有父对象的变量。所以,父对象的所有变量,对子对象都是可见的,反之则不成立。
闭包就是能够读取其他函数内部变量的函数。
闭包简单理解成"定义在一个函数内部的函数"。
在本质上,闭包就是将函数内部和函数外部连接起来的一座桥梁。
闭包的用途:闭包可以用在许多地方。它的最大用处有两个,一个是前面提到的可以读取函数内部的变量,另一个就是让这些变量的值始终保持在内存中。
// 例:手写一个1到100的和的闭包
function a() {
var i = 1;
var result = 0;
return function b() {
result += i++
return i > 100 ? result : b()
}()
}
var sum = a()
console.log(sum)
4. 深拷贝和浅拷贝的区别?
答:浅拷贝只复制指向某个对象的指针,而不是复制对象的本身,新旧对象还是共享同一块内存;
深拷贝会例外创造一个一模一样的对象,新对象和原对象不共享内存,修改新对象不会改变原对象。
实现深拷贝的方法:
(1)JSON方法:var obj1 =JSON.parse(JSON.stringify(obj))
(2)函数库lodash的_.cloneDeep方法:var _ = require('lodash') var obj1 = _.cloneDeep(obj)
(3)递归实现深拷贝:
function copy(object) {
// 判断传入的参数是数组还是对象
let target = object instanceof Array ? [] : {}
for (const [k ,v] of Object.entries(object)) {
target[k] = typeof v == 'object' ? copy(v) : v
}
return target
}
var obj1 = copy(obj)
console.log(obj.a.d === obj1.a.d);//false
ES5:
1. 原型和原型链?
答:Javascript对象都有一个叫做原型的公共属性,属性名是__proto__。这个原型属性是对另一个对象的引用,通过这个原型属性我们就可以访问另一个对象所有的属性和方法。
每一个实例对象都有一个私有属性__proto__,指向它的构造函数的原型对象(prototype)。原型对象也有自己的__proto__,层层向上直到一个对象的原型对象为null。这一层层原型就是原型链。
2. 事件冒泡、事件捕获、事件委托?
答:事件委托原理:事件在被触发之后,会逐级冒泡并能被父级元素捕捉到。这样就只需要在父级元素上绑定一份处理器,就可以处理所有子元素触发的事件。典型例子就是每个li中的事件就可以绑定在ul上,这样避免了重复绑定。
事件委托依靠的就是事件冒泡和捕获的机制。事件委托又称之为事件代理。
事件冒泡会从当前触发的事件目标一级一级往上传递,依次触发,直到document为止。
事件捕获会从document开始触发,一级一级往下传递,依次触发,直到真正事件目标为止。
document.getElementById('box3').addEventListener('click', sayBox3, false);
removeEventListener()
// 阻止冒泡 event.stopPropagation();
IE: // 添加事件,事件流固定为冒泡 attachEvent(事件名,事件处理函数)
// 删除事件 detachEvent(事件名,事件处理函数)
// IE里阻止冒泡 window.event.cancelBubble = true;
3. 数组的一些方法?
答:https://www.cnblogs.com/sqh17/p/8529401.html
4. 变量提升,作用域?
答:https://www.cnblogs.com/dennisj/p/12900097.html
变量提升指的是,无论是哪里的变量在一个范围内声明的,那么JavaScript引擎会将这个声明移到范围的顶部。
(1)作用域其实就是一个变量绑定的有效范围。
(2)JS使用的是静态作用域,即一个函数使用的变量如果没在自己里面,会去定义的地方查找,而不是去调用的地方查找。去调用的地方找到的是动态作用域。
(3)var变量会进行申明提前,在赋值前可以访问到这个变量,值是undefined。
(4)函数申明也会被提前,而且优先级比var高。
(5)使用var的函数表达式其实就是一个var变量,在赋值前调用相当于undefined(),会直接报错。
(6)let和const是块级作用域,有效范围是一对{}。
(7)同一个块级作用域里面不能重复申明,会报错。
(8)块级作用域也有“变量提升”,但是行为跟var不一样,块级作用域里面的“变量提升”会形成“暂时性死区”,在申明前访问会直接报错。
(9)使用let和const可以很方便的解决循环中异步调用参数不对的问题。
(10)let和const在全局作用域申明的变量不会成为全局对象的属性,var会。
(11)访问变量时,如果当前作用域没有,会一级一级往上找,一直到全局作用域,这就是作用域链。
(12)try...catch的catch块会延长作用域链,往最前面添加一个错误对象。
(13)with语句可以手动往作用域链最前面添加一个对象,但是严格模式下不可用。
(14)如果开发环境支持ES6,就应该使用let和const,不要用var。
5. 创建对象的5种方法?
答:https://www.cnblogs.com/Joans/p/4754346.html
6. eventloop时间循环机制?
答:同步就是代码主体一行一行的去执行;异步则是在回调用去等待他的执行结果返回给我。
eventloop 分为两个线程 一个是主线程一个是Event Table 线程。主线程则先执行同步函数执行完以后再等待EventTable线程执行异步函数的回调。
eventloop就是出现了先执行 同步 再等待异步的情况。
事件循环机制的核心是事件触发线程,由于执行栈产生异步任务,异步任务完成后事件触发线程将其回调函数传入到任务队列中,当执行栈为空,任务队列将队列头的回调函数入执行栈,从而新的一轮循环开始。这就是称为循环的原因。
ES6:
1. 更新了哪些语法?项目中实际使用?
答:(1)数组的方法
fill() : 数组填充
Array.fill(value) : 会对数组填充数组成员, 填充长度等于数组长度
Array.fill(value, start, end) : 可以使用指定的元素填充数组
includes():数组包含
Array.includes() 方法用来判断一个数组是否包含一个指定的值,根据情况,如果包含则返回 true,否则返回false。对NaN一样有效。
from():数组转换
将类数组对象转换为数组
所谓类数组对象,即必须包含length属性,且元素属性名必须是数值或者可以转换成数值的字符。
将字符串转换成数组
将map结构转换成数组,推荐使用扩运算符(...)。
将set对象的元素转换成数组
copyWithin():数组覆盖
Array.prototype.copyWithin(target, start = 0, end = this.length)
它接受三个参数:
target (必需):从该位置开始替换数据。
start (可选):从该位置开始读取数据,默认为 0 。如果是负数,start 将从末尾开始计算。
end (可选):到该位置前停止读取数据,默认等于数组长度。copyWithin 将会拷贝到该位置,但不包括 end 这个位置的元素。如果是负数, end 将从末尾开始计算。
(2)字符串方法
includes():返回布尔值,表示是否找到了参数字符串。
startsWith():返回布尔值,表示参数字符串是否在源字符串的头部。
endsWith():返回布尔值,表示参数字符串是否在源字符串的尾部。
第二个参数,表示开始搜索的位置。(起始值为0)
const s = 'zfpx';
console.log(s.startsWith('p',2)); // true
console.log(s.endsWith('f',2)); // true
console.log(s.includes('f',2)); // false
注意:endsWith的行为与其他两个方法有所不同,它针对前n个字符。而其他两个方法针对从第n个位置直到字符串结束。
repeat():返回一个新字符串,表示将原字符串重复n次。
(3)模板字符串
模版字符串,用`(反引号)标识,用${}将变量括起来。
(4)箭头函数
箭头函数根本没有自己的this,导致内部的this就是外层代码块的this。 正是因为它没有this,从而避免了this指向的问题。
(5)解构赋值
解构赋值的作用是可以快速取得数组或对象当中的元素或属性,而无需使用arr[x]或者obj[key]等传统方式进行赋值。
数组
const color = ['red', 'blue'];
//传统赋值方式
var first = color[0];
var second = color[1];
//解构赋值方式
const [first, second] = color;
console.log(first); //'red'
console.log(second); //'blue'
对象
const people = {
name: 'lux',
age: 20,
education: {
degree: 'Masters'
}
}
const { name} = people;
console.log(name); //lux
const { name, age } = people;
console.log(`${name}--${age}`) ;//lux--20
const {education: {degree}} = user;
console.log(degree); // Masters
(6)扩展运算符
对象的赋值:(如果有重复的属性名,等号右边会覆盖等号左边)
const obj = { a: 1, b: 2}
const obj2 = { ...obj } // => { a: 1, b: 2 }
const obj3 = { ...obj, b: 3, c: 4} // => { a: 1, b: 3, c: 4 },覆盖了 b,新增了 c
数组的深拷贝:
const arr2 = arr;
const arr3 = [...arr];
console.log(arr===arr2); //true, 说明arr和arr2指向同一个数组
console.log(arr===arr3); //false, 说明arr3和arr指向不同数组
合并数组:
const arr1 = [1,2];
const arr2 = [3,4];
const arr3 = [5,6];
console.log([...arr1, ...arr2, ...arr3]); //[1, 2, 3, 4, 5, 6]
将一个数组变成参数序列:(代替了apply)
console.log(Math.max(...[654, 233, 727]))//相当于console.log(Math.max(654, 233, 727))
将字符串转换为数组:
[...'hello'] // [ "h", "e", "l", "l", "o" ]
map结构:
const map = new Map();
map.set('k1', 1);
console.log(map) // Map(1) {"k1" => 1}
console.log(...map) //["k1", 1]
let map = new Map([
[1, 'one'],
[2, 'two'],
[3, 'three'],
]);
let arr = [...map];
console.log(...map.keys()); // [1, 2, 3]
2. 箭头函数和普通函数的区别?this的指向?
答:箭头函数是匿名函数不能作为构造函数,不能使用new;
箭头函数的this永远指向的是上下文的this,没有办法改变this指向,普通函数的this 指向调用它的对象。
3. promise和async/await的区别?解决了哪些问题?优缺点?还有哪些解决异步的方式?
答:https://blog.youkuaiyun.com/qq_37617413/article/details/90637694
https://www.jianshu.com/p/3a37272de675
promise的用法:
Promise,简单来说就是一个容器,里面保存着某个未来才会结束的时间(通常是一个异步操作的结果)
基本语法:
let p = new Promise((resolve,reject) => {
//...
resolve('success')
});
p.then(result => {
console.log(result);//success
});
promise共有三个状态: pending(执行中)、success(成功)、rejected(失败)
链式调用
错误捕获:
Promise.prototype.catch用于指定Promise状态变为rejected时的回调函数,可以认为是.then的简写形势,返回值跟.then一样
`let p = new Promise((resolve,reject) => {
reject('error');
});
p.catch(result => {
console.log(result);
})`
async、await
简介:异步编程的最高境界就是不关心它是否是异步。async、await很好的解决了这一点,将异步强行转换为同步处理。
async/await与promise不存在谁代替谁的说法,因为async/await是寄生于Promise,Generater的语法糖。
用法:async用于申明一个function是异步的,而await可以认为是async wait的简写,等待一个异步方法执行完成。
规则:
1 async和await是配对使用的,await存在于async的内部。否则会报错
2 await表示在这里等待一个promise返回,再接下来执行
3 await后面跟着的应该是一个promise对象,(也可以不是,如果不是接下来也没什么意义了…)
写法:
`async function demo() {
let result01 = await sleep(100);
//上一个await执行之后才会执行下一句
let result02 = await sleep(result01 + 100);
let result03 = await sleep(result02 + 100);
// console.log(result03);
return result03;
}
demo().then(result => {
console.log(result);
});`
错误捕获:
如果是reject状态,可以用try-catch捕捉
let p = new Promise((resolve,reject) => {
setTimeout(() => {
reject('error');
},1000);
});
async function demo(params) {
try {
let result = await p;
}catch(e) {
console.log(e);
}
}
demo();
区别:
1 promise是ES6,async/await是ES7
2 async/await相对于promise来讲,写法更加优雅
3 reject状态:
1)promise错误可以通过catch来捕捉,建议尾部捕获错误,
2)async/await既可以用.then又可以用try-catch捕捉
async/await优点一:它做到了真正的串行的同步写法,代码阅读相对容易
async/await优点二:对于条件语句和其他流程语句比较友好,可以直接写到判断条件里面
async/await优点三:同样的,处理复杂流程时,在代码清晰度方面有优势
欢迎继续补充哦~~~