1、什么是Promise?
Promise可以帮助我们更好地处理异步操作。
new Promise((resolve, reject) =>
{
setTimeout(() =>
{
resolve('result');
}, 100)
})
.then(console.log)
.catch(console.error);
2、async awite 是什么?运行时机呢
async function testSometing () {
console.log('2执行testSometing')
return 'testSometing'
}
async function testAsync () {
console.log('6执行testAsync')
return Promise.resolve('hello async')
};
async function test () {
console.log('1 test start...')
const v1 = await testSometing()
console.log(v1 + '5') // 5
const v2 = await testAsync()
console.log(v2 + '8') // 8
console.log(v1, v2 + '9') // 9
};
test()
let promise = new Promise(resolve => {
const a = {}
const b = JSON.parse(a)
console.log('3promise start..')
resolve(b)
})
promise.then(val => console.log(val)).catch(err => console.log(err)) // 7
console.log('4test end...')
熟悉promise 和 async/awite 异同:
都是解决js异步回调问题,promise内部无法进行try catch 只能使用原型上的catch方式统一进行代码捕获,而且promise内部无法进行断点调试。 async/awite 内部可以try catch可以断点。
3、ES6 set map 函数
const set = new Set([1, 2, 3, 4, 4]);
[...set]
上面代码通过add方法向 Set 结构加入成员,结果表明Set 结构不会添加重复的值
数组去重:
方法一:
[...new Set(array)]
方法二:
function dedupe(array) {
return Array.from(new Set(array));
}
dedupe([1, 1, 2, 3]) // [1, 2, 3]
将类数组对象转换成数组:
let arrayLike = {
0: 'tom',
1: '65',
2: '男',
3: ['jane','john','Mary'],
'length': 4
}
let arr = Array.from(arrayLike)
console.log(arr) // ['tom','65','男',['jane','john','Mary']]
要将一个类数组对象转换为一个真正的数组,必须具备以下条件:
1、该类数组对象必须具有length属性,用于指定数组的长度。如果没有length属性,那么转换后的数组是一个空数组。
2、该类数组对象的属性名必须为数值型或字符串型的数字
ps: 该类数组对象的属性名可以加引号,也可以不加引号
3.1 set 方法
add(value):添加某个值,返回Set结构本身。
delete(value):删除某个值,返回一个布尔值,表示删除是否成功。
has(value):返回一个布尔值,表示该值是否为Set的成员。
clear():清除所有成员,没有返回值
keys():返回键名的遍历器
values():返回键值的遍历器
entries():返回键值对的遍历器
forEach():使用回调函数遍历每个成员
扩展运算符(...)内部使用for...of循环,所以也可以用于 Set 结构。
使用 Set 可以很容易地实现并集(Union)、交集(Intersect)和差集(Difference)。
let a = new Set([1, 2, 3]);
let b = new Set([4, 3, 2]);
// 并集
let union = new Set([...a, ...b]);
// Set {1, 2, 3, 4}
// 交集
let intersect = new Set([...a].filter(x => b.has(x)));
// set {2, 3}
// 差集
let difference = new Set([...a].filter(x => !b.has(x)));
3.2 MAP方法:
Map有size()属性,查看Map对象大小,set(key , value) , get(Key), delete(key) , has(key) ,clear()方法。
遍历方法
Map 结构原生提供三个遍历器生成函数和一个遍历方法。
keys():返回键名的遍历器。
values():返回键值的遍历器。
entries():返回所有成员的遍历器。
forEach():遍历 Map 的所有成员。
4、 闭包、原型链等概念的理解
4.1、什么是闭包
《高级程序设计》上,这样说:当在函数内部定义了其他函数时候,就创建了闭包。闭包有权访问包含函数内部的所有变量。通俗的说,闭包就是作用域范围,因为js是函数作用域,所以函数就是闭包.更多的应用其实是在内嵌函数,这就会涉及到内嵌作用域,也叫作用域链.如果子作用域里的函数引用了父作用域的函数,这就会带来另外一个问题,什么时候引用结束?如果不结束,就会一直占用内存,过多引用会引起内存泄漏。
4.2、对原型链的理解
当调用某种方法或查找某种属性时,首先会在自身调用和查找,如果自身并没有该属性或方法,则会去它的__proto__属性中调用查找,也就是它构造函数的prototype中调用查找。
由于__proto__是任何对象都有的属性,而js里万物皆对象,所以会形成一条__proto__连起来的链条,递归访问__proto__必须最终访问到头,并且值为null。
1.查找属性,如果本身没有,则会去__proto__中查找,也就是构造函数的显式原型中查找,如果构造函数中也没有该属性,因为构造函数也是对象,也有__proto__,那么会去它的显式原型中查找,一直到null,如果没有则返回undefined
2.p.proto.constructor == function Person(){}
3.p._proto.proto== Object.prototype
4.proto
5.通过__proto__形成原型链而非protrotype
利用原型让一个引用类型继承另一个引用类型的属性和方法。
每个构造函数都有一个原型对象,原型对象都包含一个指向构造函数的指针(constructor),而实例对象都包含一个指向原型对象的内部指针(__proto__)。
如果让原型对象等于另一个类型的实例,此时的原型对象将包含一个指向另一个原型的指针(__proto__),另一个原型也包含着一个指向另一个构造函数的指针(constructor)。假如另一个原型又是另一个类型的实例……这就构成了实例与原型的链条。
图解:
5 、JS继承的实现方式?
拷贝自www.cnblogs.com/humin/p/455…文章。
// 定义一个动物类
function Animal (name) {
// 属性
this.name = name || 'Animal';
// 实例方法
this.sleep = function(){
console.log(this.name + '正在睡觉!');
}
}
// 原型方法
Animal.prototype.eat = function(food) {
console.log(this.name + '正在吃:' + food);
};
5.1 原型链继承
function Cat(){
}
Cat.prototype = new Animal(); // 关键代码
Cat.prototype.name = 'cat';
// Test Code
var cat = new Cat();
console.log(cat.name); // cat
console.log(cat.eat('fish')); // cat正在吃:fish
console.log(cat.sleep());//cat正在睡觉!
console.log(cat instanceof Animal); //true
console.log(cat instanceof Cat); //true
特点:
非常纯粹的继承关系,实例是子类的实例,也是父类的实例
父类新增原型方法/原型属性,子类都能访问到
简单,易于实现
缺点:
要想为子类新增属性和方法,必须要在new Animal()这样的语句之后执行,不能放到构造器中
无法实现多继承
来自原型对象的引用属性是所有实例共享的
创建子类实例时,无法向父类构造函数传参
5.2、构造继承
function Cat(name){
Animal.call(this);
this.name = name || 'Tom';
}
// Test Code
var cat = new Cat();
console.log(cat.name); // Tom
console.log(cat.sleep()); // Tom正在睡觉!
console.log(cat instanceof Animal); // false
console.log(cat instanceof Cat); // true
特点:
解决了原型链继承中,子类实例共享父类引用属性的问题
创建子类实例时,可以向父类传递参数
可以实现多继承(call多个父类对象)
缺点:
实例并不是父类的实例,只是子类的实例
只能继承父类的实例属性和方法,不能继承原型属性/方法
无法实现函数复用,每个子类都有父类实例函数的副本,影响性能
5.3、实例继承
function Cat(name){
var instance = new Animal();
instance.name = name || 'Tom';
return instance;
}
// Test Code
var cat = new Cat();
console.log(cat.name);
console.log(cat.sleep());
console.log(cat instanceof Animal); // true
console.log(cat instanceof Cat); // false
特点:
不限制调用方式,不管是new 子类()还是子类(),返回的对象具有相同的效果
缺点:
实例是父类的实例,不是子类的实例
不支持多继承
5.4、拷贝继承
function Cat(name){
var animal = new Animal();
for(var p in animal){
Cat.prototype[p] = animal[p];
}
Cat.prototype.name = name || 'Tom';
}
// Test Code
var cat = new Cat();
console.log(cat.name);
console.log(cat.sleep());
console.log(cat instanceof Animal); // false
console.log(cat instanceof Cat); // true
特点:
支持多继承
缺点:
效率较低,内存占用高(因为要拷贝父类的属性)
无法获取父类不可枚举的方法(不可枚举方法,不能使用for in 访问到)
5.5、组合继承
function Cat(name){
Animal.call(this);
this.name = name || 'Tom';
}
Cat.prototype = new Animal();
// 组合继承也是需要修复构造函数指向的。
Cat.prototype.constructor = Cat;
// Test Code
var cat = new Cat();
console.log(cat.name);
console.log(cat.sleep());
console.log(cat instanceof Animal); // true
console.log(cat instanceof Cat); // true
特点:
弥补了 构造继承 的缺陷,可以继承实例属性/方法,也可以继承原型属性/方法
既是子类的实例,也是父类的实例
不存在引用属性共享问题
可传参
函数可复用
缺点:
调用了两次父类构造函数,生成了两份实例(子类实例将子类原型上的那份屏蔽了)
6.6 、寄生组合继承
function Cat(name){
Animal.call(this);
this.name = name || 'Tom';
}
(function(){
// 创建一个没有实例方法的类
var Super = function(){};
Super.prototype = Animal.prototype;
//将实例作为子类的原型
Cat.prototype = new Super();
Cat.prototype.constructor = Cat;
})();
// Test Code
var cat = new Cat();
console.log(cat.name);
console.log(cat.sleep());
console.log(cat instanceof Animal); // true
console.log(cat instanceof Cat); //true
6、常用js方法
6.1 数组去重
数组去重:
//方法一:
Array.prototype.unique = function(){
var result = [];
this.forEach(function(v){
if(result.indexOf(v) < 0){
result.push(v);
}
});
return result;
}
//方法二:
Array.prototype.unique = function(){
var result = [],hash = {};
this.forEach(function(v){
if(!hash[v]){
hash[v] = true;
result.push(v);
}
});
return result;
}
//方法二存在隐式转换比如 1 和 "1" 会被认为是重复,所以进行升级:
Array.prototype.unique = function(){
var result = [],hash = {};
this.forEach(function(v){
var type = typeof(v); //获取元素类型
hash[v] || (hash[v] = new Array());
if(hash[v].indexOf(type) < 0){
hash[v].push(type); //存储类型
result.push(v);
}
});
return result;
}
//方法四:先排序再去重
Array.prototype.unique = function(){
var result = [this[0]];
this.sort();
this.forEach(function(v){
v != result[result.length - 1] && result.push(v); //仅与result最后一个元素比较
});
}
// es6 去重:方法一
Array.prototype.unique = function(){
return [...new Set(this)];
}
// es6 去重:方法二
Array.prototype.unique = function(){
return Array.form(new Set(this))
}
//排序算法
6.2、 计算出下面字符串出现次数最多的值?
let str = 'abcdefghigakgjisaaaganghegsa'
function getMaxVal(str) {
let strArr = [...str]
let max = 0
let obj = {}
let maxVal = ''
strArr.forEach(function(item, i){
obj[item] = obj[item] == 'undefined'? 1 : obj[item] + 1
if(obj[item] > max) {
max = obj[item]
maxVal = item
}
})
return maxVal
}
6.3、 翻转字符串
let str = 'hello lsj'
function reverseStr (str) {
return [...str].reverse().join('')
}
6.4、 翻转字符串
let str = 'hello lsj'
function reverseStr (str) {
return [...str].reverse().join('')
}
7、async和defer的区别
都是为script增减属性,它定义脚本是否异步执行。async 属性仅适用于外部脚本(只有在使用 src 属性时)有多种执行外部脚本的方法:
· 如果 async="async":脚本相对于页面的其余部分异步地执行(当页面继续进行解析时,脚本将被执行)
· 如果不使用 async 且 defer="defer":脚本将在页面完成解析时执行
· 如果既不使用 async 也不使用 defer:在浏览器继续解析页面之前,立即读取并执行脚本
· 为html元素增加manifest,开发离线web应用程序时他与API结合使用,定义一个URL,在这个URL上描述文档的缓存信息。
· 为iframe增加撒个属性,sandbox、seamless、srcdoc。用来提高页面安全性,防止不信任的web页面执行某些操作。
8、前端缓存机制和办法
9、node面试题
9.1、Cookies如何防范XSS攻击
XSS(Cross-Site Scripting,跨站脚本攻击)是指攻击者在返回的HTML中插入JavaScript脚本。为了减轻这些攻击,需要在HTTP头部配置set-cookie:
HttpOnly - 这个属性可以防止cross-site scripting,因为它会禁止Javascript脚本访问cookie。
secure - 这个属性告诉浏览器仅在请求为HTTPS时发送cookie。
10、js基础查漏补缺。
参考https://www.cnblogs.com/meteorcn/p/node_mianshiti_interview_question.html
10.1、apply, call和bind有什么区别?
三者都可以把一个函数应用到其他对象上,注意不是自身对象也就是用来改变this指向.apply,call是直接执行函数调用,bind是绑定,执行需要再次调用.apply和call的区别是apply接受数组作为参数,而call是接受逗号分隔的无限多个参数列表
10.2、** caller, callee和arguments分别是什么?**
caller, callee之间的关系就像是employer和employee之间的关系,就是调用与被调用的关系,二者返回的都是函数对象引用.arguments是函数的所有参数列表,它是一个类数组的变量.
10.3、defineProperty, hasOwnProperty, isEnumerable都是做什么用的?
Object.defineProperty(obj, prop, descriptor)用来给对象定义属性,有value,writable,configurable,enumerable,set/get等.
hasOwnProerty用于检查某一属性是不是存在于对象本身,继承来的父亲的属性不算.isEnumerable用来检测某一属性是否可遍历,也就是能不能用for..in循环来取到
10.4、列举数组相关的常用方法
push/pop,
shift/unshift,
split/join,
slice/splice/concat,
sort/reverse,
map/reduce,
forEach, filter
10.5、 列举字符串相关的常用方法
indexOf/lastIndexOf/charAt,
split/match/test,
slice/substring/substr,
toLowerCase/toUpperCase
enctype="multipart/form-data" method="post"
10.6、 文件上传需要设置什么?
enctype="multipart/form-data" method="post"
10.7、js排序方法总结。
10.8、 字符串扩展了哪些方法?
1.codePointAt() 参数:字符在字符串中的位置;返回值:返回一个字符的码点
2.String.fromCodePoint() 参数:码点 返回值:码点对应的字符
3.for....of 字符串的遍历器接口
4.nomalize() 功能:将字符的不同表示方法统一为同样的形式,称之为Unicode正规化。参数:指定normalize的方式。
5.includes()功能:字符串中是否含有子串; 参数:要查找的子串 ;返回值:布尔值,true或false。
6.startsWith()返回值:布尔类型,表示字符串是否在源字符串的头部;当有第二个参数时,表示开始搜索的位置。
7.endsWith()返回值:布尔类型,表示字符串是否在源字符串的尾部。当有第二个参数时,表示开始搜索的位置。
8.repeat():返回值:将源字符串重复n次。参数:字符串重复次数。
9.padStart()功能:如果字符串不够指定长度,会在头部补全。参数:第一个指定长度,第二个指定字符串。返回值:返回新的字符串
10.padEnd()同上,不过是在尾部
11.String.raw() 功能:用来充当模板字符串的处理函数。返回值:斜杠都被转义的字符串。
JavaScript 异步、栈、事件循环、任务队列
- 宏任务:script(全局任务), setTimeout, setInterval, setImmediate, I/O, UI rendering.
- 微任务:process.nextTick, Promise, Object.observer, MutationObserver.
浏览器的 Event Loop
浏览器的 Event Loop 遵循的是 HTML5 标准,而 NodeJs 的 Event Loop 遵循的是 libuv。 区别较大.
网站性能优化中有一条:dns与解析如