reduce用法
js数据类型?undefined和null区别?
跨域
-
- jsonp
- 代理(为什么)
- cors
-
-
-
- 请求头字段
-
-
-
-
-
-
- 浏览器直接发出CORS请求。具体来说,就是在头信息之中,增加一个Origin字段
-
-
-
-
-
-
- 响应头字段
-
-
-
-
-
-
- Access-Control-Allow-Origin
- Access-Control-Allow-Credentials
- Access-Control-Expose-Headers
-
-
-
-
-
- 复杂请求(首先发送预检请求)
-
-
-
-
- 请求头字段
-
-
-
-
-
-
- 关键字段是Origin
- Access-Control-Request-Method
- Access-Control-Request-Headers
-
-
-
-
-
-
- 响应头字段
-
-
-
-
-
-
- Access-Control-Allow-Methods
- Access-Control-Allow-Headers
- Access-Control-Allow-Credentials
- Access-Control-Max-Age
-
-
-
数组去重
(数组里面有对象怎么办?)
如何展开二维数组?
-
- 数组实例的 flat(),flatMap()
- reduce
let arr = [
[1, 2],
[3, 4],
[5, 6]
];
let flatten = arr.reduce((previous, current) => {
return previous.concat(current);
});
console.log(flatten); // [1, 2, 3, 4, 5, 6]
// ES6中也可以利用...展开运算符来实现的,实现思路一样,只是写法更精简了
flatten = arr.reduce((a, b) => [...a, ...b]);
console.log(flatten); // [1, 2, 3, 4, 5, 6]
fetch和原生ajax有什么区别?
Async与promise有什么不同?
生成器与迭代器有听过吗?
迭代器是迭代器工厂函数(具有Symbol .iterator属性的函数)返回的包含next方法的迭代器对象;
生成器其实也是一种迭代器,不过拥有了 ES6 新增的句法,使其变得更为灵活和便捷,常用来创建迭代器;
使用生成器函数可以使我们在定义一个可迭代类时变得更为简单,我们不需要一层一层编写迭代方法、迭代器、迭代结果对象等等,我们只需要编写 *Symbol.iterator 方法即可:
*[Symbol.iterator]() { // 使用生成器函数编写迭代方法
for (let x = this.lower; x < this.upper; x++) {
yield x;
}
}
// 等同于前面的例子:
[Symbol.iterator]() { // 定义其迭代方法
let next = this.lower; // 下一个返回值
let last = this.upper; // 最后的返回值
return { // 返回可迭代对象
next() { // 可迭代对象的 next() 方法,其返回值是迭代结果对象
if ( next <= last) { // 在迭代未结束时返回拥有 value 属性的迭代结果对象
return { value : next++};
} else { // 否则返回拥有 done 为 true 的迭代结果对象
return { done : true};
}
},
[Symbol.iterator]() { return this; } // 可以将迭代器本身设置为可迭代对象
}
}
声明变量的方式?
- var
- function
- let
-
- let声明的变量只在它所在的代码块有效
- 不存在变量提升
- 暂时性死区
-
-
- 块级作用域内存在let命令,它所声明的变量就“绑定”(binding)这个区域,不受外部的影响
-
-
- 不允许重复声明
- 块级作用域(代替了匿名立即执行函数表达式(匿名 IIFE))
-
-
- 函数声明(对 ES6 的浏览器实现有效,考虑到环境导致的行为差异太大,应该避免在块级作用域内声明函数。如果确实需要,也应该写成函数表达式,而不是函数声明语句。)
-
-
-
-
- 允许在块级作用域内声明函数。
- 函数声明类似于var,即会提升到全局作用域或函数作用域的头部。
- 同时,函数声明还会提升到所在的块级作用域的头部。
-
-
- const
-
- 本质
-
-
- const实际上保证的,并不是变量的值不得改动,而是变量指向的那个内存地址所保存的数据不得改动。对于简单类型的数据,值就保存在变量指向的那个内存地址;对于复合类型的数据(主要是对象和数组),变量指向的内存地址,保存的只是一个指向实际数据的指针;
-
暂时性死区的本质就是,只要一进入当前作用域,所要使用的变量就已经存在了,但是不可获取,只有等到声明变量的那一行代码出现,才可以获取和使用该变量。
- import
- class
作用域?
- ES5
-
- 全局作用域
- 块级作用域
- ES6
-
- 块级作用域
this
- 指向当前函数的执行上下文
- juejin.cn/post/721843…
处理url字符串?
找出两个数组的交集?
交换两个数组?
- 临时变量
- 解构赋值
- 异或
实现异步编程?
- callback
- 事件监听
- 发布订阅
- promise
- Generator
-
- 可以暂停执行和恢复执行,这是它能封装异步任务的根本原因;
- 函数体内外的数据交换和错误处理机制
- async\await
-
- 内置执行器
- 更好的语义
- 更广的适用性
- 返回值是 Promise
事件模型?事件冒泡?事件捕获?事件委托
- juejin.cn/post/701223…
- 事件委托原理:事件冒泡
-
- 事件委托 又叫 事件代理,指的是利用事件冒泡原理,只需给外层父容器添加事件,若内层子元素有点击事件,则会冒泡到父容器上,这就是事件委托,简单说就是:子元素委托它们的父级代为执行事件。
- 优点:
-
- 提高性能:节省内存占用,减少事件注册
- 动态监听:新增子对象时无需再次对其绑定事件,适合动态添加元素
枚举
拷贝
- 浅拷贝是拷贝一层,深层次的对象级别的就拷贝引用;但是指针指向的地址还是同一个,所以对应的变动会影响双方。
- 深拷贝是拷贝多层,每一级别的数据都会拷贝出来,递归拷贝一个对象及其属性,完成后两个对象不互相影响
- 浅拷贝
-
- object.assign(目标对象,源对象,源对象,源对象......)
-
-
- Object.assgin 只能深拷贝第一层, 深层的还是浅拷贝
- 针对深拷贝,需要使用其他办法,因为 Object.assign()拷贝的是属性值。假如源对象的属性值是一个对象的引用,那么它也只指向那个引用。
-
-
- 展开运算符(...)=>{...源对象}
- 深拷贝
-
- JSON做字符串转换:用JSON.stringify把对象转成字符串,再用JSON.parse把字符串转成新的对象。
- object.create()
function deepClone(initalObj, finalObj) {
var obj = finalObj || {};
for (var i in initalObj) {
var prop = initalObj[i]; // 避免相互引用对象导致死循环,如initalObj.a = initalObj的情况
if(prop === obj) {
continue;
}
if (typeof prop === 'object') {
obj[i] = (prop.constructor === Array) ? [] : Object.create(prop);
} else {
obj[i] = prop;
}
}
return obj;
}
-
- jquery中$.extends()
var $ = require('jquery');
var obj1 = {
a: 1,
b: { f: { g: 1 } },
c: [1, 2, 3]
};
var obj2 = $.extend(true, {}, obj1);
console.log(obj1.b.f === obj2.b.f);
// false
-
- 第三方函数lodash
- 递归拷贝
function deepClone(initalObj, finalObj) {
var obj = finalObj || {};
for (var i in initalObj) {
var prop = initalObj[i]; // 避免相互引用对象导致死循环,如initalObj.a = initalObj的情况
if(prop === obj) {
continue;
}
if (typeof prop === 'object') {
obj[i] = (prop.constructor === Array) ? [] : {};
arguments.callee(prop, obj[i]);
} else {
obj[i] = prop;
}
}
return obj;
}
var str = {};
var obj = {
a: {a: "hello", b: 21},
b:function(){console.log('aaa')},
c:[1,2,3,4,5]
};
deepClone(obj, str);
console.log(str.a);
如何删除对象中不需要的属性?
-
- 将属性设置为 undefined
- delete
- 使用对象解构
- rest参数
- 循环+判断
- JSON.stringify的第二个参数
- Reflect.deleteProperty(对象,属性名)
- lodash omit()
伪数组转换成标准数组?
-
- 声明一个空数组,通过遍历伪数组把它们重新添加到新的数组中
- 使用原型继承:把伪数组的__proto__指向数组的原型
var aLi = document.querySelectorAll('li');
console.log(aLi.constructor === Array) //false
aLi.__proto__ = Array.prototype;
console.log(aLi.constructor === Array) //true
-
- Array.prototype.slice.call();
- Array.from()
- 扩展运算符...
基本数据类型的prototype指向哪里?
构造函数的原型对象。
去抖、节流具体实现?区别?
事件循环/Promise、async、setTimeout执行顺序?
JS主线程不断的循环往复的从任务队列中读取任务,执行任务,其中运行机制称为事件循环(event loop)。
JS分为同步任务和异步任务
-
- 同步任务都在主线程上执行,形成一个执行栈
- 主线程之外,事件触发线程管理着一个任务队列,只要异步任务有了运行结果,就在任务队列之中放置一个事件。
- 一旦执行栈中的所有同步任务执行完毕(此时JS引擎空闲),系统就会读取任务队列,将可运行的异步任务添加到可执行栈中,开始执行。
- 任务队列分为 macrotasks 和 microtasks
-
-
- macrotasks(setTimeout, setInterval, setImmediate, I/O, UI rendering)
-
-
-
-
- setImmediate: www.cnblogs.com/crispyChick…
-
-
-
-
- microtasks(process.nextTick, Promises, Object.observe(废弃,Proxy 对象替代), MutationObserver)
-
-
-
-
- process.nextTick: www.cnblogs.com/crispyChick…
- MutationObserver: developer.mozilla.org/zh-CN/docs/…
-
-
-
- 事件循环每次只会入栈一个 macrotask ,主线程执行完该任务后又会先检查 microtasks 队列并完成里面的所有任务后再执行 macrotask
- github.com/sisterAn/bl…
- github.com/Advanced-Fr…
- juejin.cn/post/725217…
正则表达式?
instanceOf实现原理?
-
- 通过原型链判断:判断构造函数的原型是否在实例的原型链上,如果在返回true;反之返回false
判断数据类型?
-
- typeof
- instanceOf
- Object.prototype.toString().call().slice(8,-1).toLocaleLowerCase() // 'object'
- constructor
typeof 和 instanceof 的弊端
typeof 可以判断除了 null 以外的基础数据类型,但是判断引用类型时,除了 function 类型,其他的无法准确判断。
instanceof 可以准确地判断各种引用类型,但是不能正确判断原始数据类型。
es6继承原理?
(1)子类的__proto__属性,表示构造函数的继承,总是指向父类。
(2)子类prototype属性的__proto__属性,表示方法的继承,总是指向父类的prototype属性
寄生组合式继承
function inheritPrototype(subType, superType){
//原型式继承:浅拷贝superType.prototype对象作为superType.prototype为新对象的原型
// 内部会自带_proto_指向:prototype.__proto__ = superType.prototype;
var prototype = Object.create(superType.prototype);
// subType.prototype.__proto__ = superType.prototype;
subType.prototype = prototype; // 将子类的原型替换为这个原型
prototype.constructor = subType; // 修正原型的构造函数
}
function SuperType(name){
this.name = name;
this.colors = ["red", "blue", "green"];
}
SuperType.prototype.sayName = function(){
alert(this.name);
};
function SubType(name, age){
SuperType.call(this, name);
this.age = age;
}
// 核心:因为是对父类原型的复制,所以不包含父类的构造函数,也就不会调用两次父类的构造函数造成浪费
inheritPrototype(SubType, SuperType);
SubType.prototype.sayAge = function(){
alert(this.age);
}
babeljs对extends语法糖的部分编译结果
function _inherits(subClass, superClass) {
//对superClass进行类型判断
if (typeof superClass !== "function" && superClass !== null) {
throw new TypeError("Super expression must either be null or a function");
}
//子类的prototype继承父类的prototype
//也就是说执行后 subClass.prototype.__proto__ === superClass.prototype; 这条语句为true
subClass.prototype = Object.create(superClass && superClass.prototype, {
constructor: {
value: subClass,
writable: true,
configurable: true
}
});
//子类是父类构建出的函数对象,需要指定对象的__proto__
if (superClass) _setPrototypeOf(subClass, superClass);
}
- 参考
Js原生操作dom的方法?
如何阻塞代码3秒后执行?
import为什么不能在条件中使用?
import命令是编译阶段执行的,在代码运行之前。
promise
- promise解决了哪些问题?
- 如何实现promise?
js为什么是单线程的?
-
- 所谓单线程,是指在JS引擎中负责解释和执行JS代码的线程唯一,同一时间上只能执行一件任务
- 原因:避免DOM渲染的冲突
-
-
- 浏览器需要渲染DOM
- JS可以修改DOM结构
- JS执行时,浏览器DOM渲染停止
- 两段JS不能同时执行(都修改DOM就冲突了)
- web worker支持多线程,但是不能访问DOM
-
js设计模式有了解过吗?
箭头函数与普通函数有什么不同?
-
- 函数体内的this对象,就是定义时所在的对象,而不是使用时所在的对象。
- 不可以当作构造函数,也就是说,不可以使用new命令,否则会抛出一个错误。
- 不可以使用arguments对象,该对象在函数体内不存在。如果要用,可以用 rest 参数代替。
- 不可以使用yield命令,因此箭头函数不能用作 Generator 函数。
- this指向的固定化,并不是因为箭头函数内部有绑定this的机制,实际原因是箭头函数根本没有自己的this,导致内部的this就是外层代码块的this。正是因为它没有this,所以也就不能用作构造函数。
- 箭头函数不能使用 arguments、super 和 new.target,也不能用作构造函数。此外,箭头函数也没有 prototype 属性
闭包是什么?有什么作用?
-
- 原理在于闭包函数的作用域链中引用了包含函数的作用域。
- 闭包就是能够读取其他函数内部变量的函数。
-
-
- 可以实现封装,属性私有化
- 模块化开发,防止污染全局变量
-
js模块化开发?
-
- 存在命名冲突、代码复杂性的问题
- 模块成员之间看不出直接关系
- 命名空间
-
- 使用的仍然是全局变量
- 作用: 减少了全局变量,降低了命名冲突
- 问题: 数据不安全(外部可以直接修改模块内部的数据),无法按需导出
- 立即执行函数IIFE
-
- 创建闭包来封装私有变量
- 作用:解决全局命名冲突问题,模拟模块的功能;解决按需导出;模块依赖关系明显
- 问题:每次引入的script需要严格按照顺序引入
- CommonJS
-
- 同步加载, 服务器端,运行时,输出的是值的拷贝
- 作用:
-
-
- 不需要依赖script加载的顺序,模块加载的顺序,按照代码中require引入的顺序
- 所有代码都运行在模块作用域,不会污染全局作用域
- 模块可以多次加载,在第一次加载时运行,之后加载,读取缓存结果。要想让模块再次运行,必须清除缓存。
-
-
- 问题:
-
-
- 文件加载是同步运行,加载时间长
- 不适用客户端,因为客户端都是通过网络进行下载,如果依赖过多,导致页面非常卡顿。
-
- AMD
-
- 异步加载,运行时
- 延迟加载,依赖前置
- 作用: 使用异步加载模块,提高加载性能
- 问题: 如果引入了多余的依赖,没有进行区分是否调用,都会进行加载
- CMD
-
- 异步加载,运行时,
- 依赖就近,按需加载
- UMD
-
- 解决不同模块加载器和环境之间的兼容性问题
- ESM
-
- 输出的是值的引用
- 编译时输出接口
作用域和作用域链?
每个函数都有自己的执行环境=>每个环境都有相关联的变量对象(包含环境中定义的变量和函数,由于这个变量对象只在函数执行的时候才被创建,因此也称之为“活动对象”)=>“作用域链”则是指向这些变量对象的指针列表=>这张表里除了保存当前作用域的引用,还保存了当前作用域上层以及上上层等作用域的引用(如果有的话),最终到达“全局作用域
es5中的构造函数和es6中的class有什么区别?
-
- 在es5中,原型上定义的方法是可枚举的;es6中class里面的方法是不可枚举的;
- 类必须使用new调用,否则会报错。这是它跟普通构造函数的一个主要区别,后者不用new也可以执行
- 类不存在变量提升(hoist)
- ES5的继承,实质是先创造子类的实例对象this,然后再将父类的方法添加到this上(Parent.apply(this))。ES6的继承实质是先将父类实例对象的属性和方法,加到this上面(所以必须先调用super方法),然后再用子类的构造函数修改this。
如何理解面向对象?
创建一个对象会经历哪些过程?
-
- 创建一个新对象;
- 设置原型链(空对象的原型指向构造函数的原型,继承构造函数的原型对象上的公有属性和方法)
- 将构造函数的作用域赋给新对象(因此this就指向了这个对象,继承构造函数中的属性);
- 返回新对象(判断返回值的类型,如果是值类型就是返回新的创建对象,如果是引用类型就返回引用类型的对象)
function newObject(ctor, args) {
let obj = Object.create(ctor.prototype)
let res = ctor.apply(obj, args)
let isObject = typeof res === 'object' && res !== null
let isFunction = typeof res === 'function'
return isObject || isFunction ? res : obj
}
function sup(a, b){
this.a = a
this.b = b
}
sup.prototype.say = function() {
console.log(this.a + this.b)
}
s = newObject(sup, ['str', '__'])
console.log(JSON.stringify(s))
s.say()
s2 = new sup('str2','__')
console.log(JSON.stringify(s2))
s2.say()
如何判断数据类型为数组?
- variable instanceOf Array
- Object.prototype.toString.call(variable)
- Array.prototype.isPrototypeOf(variable)
- variable.constructor.toString().indexOf("Array") !== -1
- Array.isArray(variable);