1.JS数据类型
- 基本数据类型:String Number Null Underfine boolean Symbol(es6) 利用symbol.for()判断是否存在唯一值,存在则返回该值,不然则创建新的值。 const sy = Symbol('key') Symbol.for 首先会在全局搜索被登记的 Symbol 中是否有该字符串参数作为名称的 Symbol 值,如果有即返回该 Symbol 值,若没有则新建并返回一个以该字符串参数为名称的 Symbol 值,并登记在全局环境中供搜索。 Symbol.keyFor 返回一个已登记的 Symbol 类型值的 key ,用来检测该字符串参数作为名称的 Symbol 值是否已被登记。 引用数据类型:object - function、arrary、 {}
基本数据类型保存在栈中,引用数据类型同时保存在栈和堆中,基本数据类型不能添加属性
这里有详解:blog.csdn.net/xiasohuai/a… segmentfault.com/a/119000002…
在js的最初版本中,使用的是32位系统,为了性能考虑使用地位存储了变量的类型信息,会在信息的机器码的地位1-3位存储其类型信息,如:
000 ----- 对象
010 ----- 浮点数
100 ----- 字符串
110 ----- 布尔型
1 ----- 整数
- 数据类型转换:
1.toString() null 和 underfine 会报错
2.String() 可以将所有类型转换为字符串
3.'+' 运算符,叫连接运算符 ,一边不是字符会转换成字符
4.boolean 转换成布尔值 除了 null underfine +-0 '' NAN 为false,其他都为true
5.取反运算符 !会将值转为布尔值,!!为其本身的布尔值( !!3->true,!3->false)
6.Number 将任意值转换为数值 (perseInt() 、perseFloat())
2.数组方法
map: 遍历数组,返回回调返回值组成的新数组,不改变原数组,返回执行操作后的新数组。
forEach: 无法break,对数组的每一项都执行操作,会改变原数组,没有返回值,返回为underfine。
filter: 过滤,创建一个新数组,返回符合条件的新数组。
some: 有一项返回true,则整体为true
every: 有一项返回false,则整体为false
join: 通过指定连接符生成字符串
push / pop: 末尾推入和弹出,改变原数组, 返回推入/弹出项【有误】
unshift / shift: 头部推入和弹出,改变原数组,返回操作项【有误】
sort(fn) / reverse: 排序与反转,改变原数组
concat: 连接数组,不影响原数组, 浅拷贝
slice(start, end): 返回截断后的新数组,不改变原数组
splice(start, number, value...): 返回删除元素组成的数组,value 为插入项,改变原数组
indexOf / lastIndexOf(value, fromIndex): 查找数组项,返回对应的下标
reduce / reduceRight(fn(prev, cur), defaultPrev): 数组累加器,两两执行,prev
为上次化简函数的return值,cur 为当前值(从第二项开始)
3.map和forEach区别
forEach:无返回值,不能break。
map: 有返回值,必须返回。
4.闭包
- 闭包,可以间接访问函数内部的变量,函数A里面有一个函数B,函数B可以访问到函数A内部的变量,函数B就叫闭包。
- 作用:
1.访问函数内部的变量:解决多个函数依赖一个变量,但如果变量定义在全局会污染全局,不利于更新和维护。
2.保留词法作用域,让变量一直保存在内存中:最经典的就是解决for循环 var定义函数的问题
for (var i = 1; i <=5; i++) {
(function(j) {
// j = 1
// j = 2 保留词法作用域
// j = 3
setTimeout(function timer() {
console.log(j)
}, j * 1000)
})(i)
}
弊端:函数执行后,不会执行js垃圾回收机制,易造成内存泄漏
参考:www.cnblogs.com/amcy/p/9912…
js垃圾回收机制:segmentfault.com/a/119000001…
5.this指向
- 直接调用函数指向window
- 函数被其他对象或变量调用,this指向该对象
- 对于new 来说,this被绑定在new出来的实例上
- 箭头函数本身没有this,箭头函数this指向他外层第一个非箭头函数。箭头函数不能用bind等动态改变this作用域,一旦绑定不能被任何方式所改变。
let a = {}
let fn = function() {
console.log(this)
}
fn.bind().bind().bind(a) // 输出window
let fn2 = function() {
return function() {
return fn.apply() //fn永远和第一个绑定,其他无关
}.apply(a)
}
fn2()
- 当多种规则的this同时出现,遵循优先级 new > bind等 > obj.fn() > 直接调用
6.EventLoop
- 线程和进程
- 执行栈和任务队列
- 微任务(microtask, ES6叫jobs)和宏任务(mactask, ES6叫task )
- 宏任务macro-task包括:script setTimeout, setInterval, setImmediate, I/O, UI rendering。
- 微任务micro-task包括:process.nextTick, Promises, Object.observe, MutationObserver。
console.log('script start');
setTimeout(function () {
console.log('setTimeout---0');
}, 0);
setTimeout(function () {
console.log('setTimeout---200');
setTimeout(function () {
console.log('inner-setTimeout---0');
});
Promise.resolve().then(function () {
console.log('promise5');
});
}, 200);
Promise.resolve().then(function () {
console.log('promise1');
}).then(function () {
console.log('promise2');
});
Promise.resolve().then(function () {
console.log('promise3');
});
console.log('script end');
结果:
script start
script end
promise1
promise3
promise2
setTimeout---0
setTimeout---200
promise5
inner-setTimeout---0
7.promise
1)Promise基本特性
1、Promise有三种状态:pending(进行中)、fulfilled(已成功)、rejected(已失败)
2、Promise对象接受一个回调函数作为参数, 该回调函数接受两个参数,分别是成功时的回调resolve和失败时的回调reject;另外resolve的参数除了正常值以外, 还可能是一个Promise对象的实例;reject的参数通常是一个Error对象的实例。
3、then方法返回一个新的Promise实例,并接收两个参数onResolved(fulfilled状态的回调);onRejected(rejected状态的回调,该参数可选)
4、catch方法返回一个新的Promise实例
5、finally方法不管Promise状态如何都会执行,该方法的回调函数不接受任何参数
6、Promise.all()方法将多个多个Promise实例,包装成一个新的Promise实例,该方法接受一个由Promise对象组成的数组作为参数(Promise.all()方法的参数可以不是数组,但必须具有Iterator接口,且返回的每个成员都是Promise实例),注意参数中只要有一个实例触发catch方法,都会触发Promise.all()方法返回的新的实例的catch方法,如果参数中的某个实例本身调用了catch方法,将不会触发Promise.all()方法返回的新实例的catch方法
7、Promise.race()方法的参数与Promise.all方法一样,参数中的实例只要有一个率先改变状态就会将该实例的状态传给Promise.race()方法,并将返回值作为Promise.race()方法产生的Promise实例的返回值
8、Promise.resolve()将现有对象转为Promise对象,如果该方法的参数为一个Promise对象,Promise.resolve()将不做任何处理;如果参数thenable对象(即具有then方法),Promise.resolve()将该对象转为Promise对象并立即执行then方法;如果参数是一个原始值,或者是一个不具有then方法的对象,则Promise.resolve方法返回一个新的Promise对象,状态为fulfilled,其参数将会作为then方法中onResolved回调函数的参数,如果Promise.resolve方法不带参数,会直接返回一个fulfilled状态的 Promise 对象。需要注意的是,立即resolve()的 Promise 对象,是在本轮“事件循环”(event loop)的结束时执行,而不是在下一轮“事件循环”的开始时。
9、Promise.reject()同样返回一个新的Promise对象,状态为rejected,无论传入任何参数都将作为reject()的参数
2)Promise优点
①统一异步 API
Promise 的一个重要优点是它将逐渐被用作浏览器的异步 API ,统一现在各种各样的 API ,以及不兼容的模式和手法。
②Promise 与事件对比
和事件相比较, Promise 更适合处理一次性的结果。在结果计算出来之前或之后注册回调函数都是可以的,都可以拿到正确的值。 Promise 的这个优点很自然。但是,不能使用 Promise 处理多次触发的事件。链式处理是 Promise 的又一优点,但是事件却不能这样链式处理。
③Promise 与回调对比
解决了回调地狱的问题
new Promise((resolve,reject) => {
setTimeout(() => {
console.log(1)
resolve()
},1000)
}).then((res) => {
setTimeout(() => {
console.log(2)
},2000)
}).then((res) => {
setTimeout(() => {
console.log(3)
},3000)
}).catch((err) => {
console.log(err)
})
//generator
function *foo(x) {
let y = 2 * (yield (x + 1))
let z = yield (y / 3)
return (x + y + z)
}
let it = foo(5)
console.log(it.next()) // => {value: 6, done: false}
console.log(it.next(12)) // => {value: 8, done: false}
console.log(it.next(13)) // => {value: 42, done: true}
8.数组去重
- 1.先创建一个新数组,sort原数组,然后for循环比较当前index的value和下一个index的value,不同则把当前index值push进新数组,然后i++,最后返回新数组。
- 2.filter加indexOf
Array.prototype.unique = function () {
return this.filter((item, index) => {
return this.indexOf(item) === index;
})
}
- 3.Set 方法
const arr = [1,3,2,4,3,2,5]
new Set(arr)
输出:Set(5) {1, 3, 2, 4, 5}
function unique(arr) {
if (!Array.isArray(arr)) {
console.log('type error!')
return
}
return [...new Set(arr)]
}
- 4.includes()方法
- 5.reduce遍历数组
function unique(arr) {
return arr.reduce((pre, cur) => {
!pre.includes(cur) && pre.push(cur)
return pre
}, [])
}
9.原型和原型链
- 原型:可以叫原始对象,没错原型也是一个对象,每个js对象有一个_proto_属性指向他的原型,可以访问到对象的内部属性
- 原型链:当查找一个属性或方法时,如果在当前对象中找不到定义,会继续在当前对象的原型对象中查找;如果原型对象中依然没有找到,会继续在原型对象的原型中查找(原型也是对象,也有它自己的原型);如此继续,直到找到为止,或者查找到最顶层的原型对象中也没有找到,就结束查找,返回undefined。可以看出,这个查找过程是一个链式的查找,每个对象都有一个到它自身原型对象的链接,这些链接组件的整个链条就是原型链。
// 总结:
// Object 是所有对象的原型,所有对象都可以通过 __proto__ 找到它
// Function 是所有函数的原型,所有函数都可以通过 __proto__ 找到它
// prototype 是函数特有的属性,指向他的原型
// 对象的 __proto__ 属性指向原型, __proto__ 将对象和原型连接起来组成原型链
// 实例的_proto_指向类或构造的 prototype
const O1 = new Object
O1.__proto__ === Object.prototype
true
10.防抖和节流
- 防抖:将多次事件合并为一次执行,在规定时间内事件再次触发则重新计时。
function debounce(fn, wait) {
var timeout = null;
return function() {
if(timeout !== null)
clearTimeout(timeout);
timeout = setTimeout(fn, wait);
}
}
// 处理函数
function handle() {
console.log(Math.random());
}
// 滚动事件
window.addEventListener('scroll', debounce(handle, 1000));
- 节流:在规定时间内事件只执行一次
var throttle = function(func, delay) {
var prev = Date.now();
return function() {
var context = this;
var args = arguments;
var now = Date.now();
if (now - prev >= delay) {
func.apply(context, args);
prev = Date.now();
}
}
}
function handle() {
console.log(Math.random());
}
window.addEventListener('scroll', throttle(handle, 1000));
11.拷贝
- 浅拷贝:Object.assign(target,source)
function shallowClone(obj) {
let copy = obj instanceof Array ? [] : {}
for(let i in obj) {
if(obj.hasOwnProperty(i)) {
copy[i] = obj[i]
}
}
return copy
}
- 深拷贝: JSON.parse(JSON.stringfy(source))
function deepClone(obj) {
let copy = obj instanceof Array ? [] : {}
for(let i in obj) {
if(obj.hasOwnProperty(i)) {
copy[i] = typeof obj[i] === 'object' ? deepClone(obj[i]) : obj[i]
}
}
return copy
}
12.call,apply,bind
1.call 第二个参数为一组,多个参数。
2.apply 第二个参数为一个数组,多个参数要都放在这个数组里。
3.bind 第二个参数和call apply类似,只不过bind之后返回一个改变上下文的函数,不会执行,需要再后面加()才执行。
13.数据类型判断
- typeof:能正确判断基本数据类型,除了null会判定为object,引用数据类型,除了function,其他都只能判断为object,不能准确判断。
- instanceof:能正确判断对象数据类型,内部机制是通过判断对象的原型链中是不是能找到类型的 prototype。
function _instanceof(left,right) {
let leftValue = left._proto_;
let rightValue = right.prototype;
while(true) {
if(leftValue === null){
return false
}
if(rightValue === leftValue){
return true
}
leftValue = leftValue._proto_
}
}
- Object.prototype.toString.call()
Object.prototype.toString.call([]); // => [object Array]
Object.prototype.toString.call({}); // => [object Object]
Object.prototype.toString.call(''); // => [object String]
Object.prototype.toString.call(new Date()); // => [object Date]
Object.prototype.toString.call(1); // => [object Number]
Object.prototype.toString.call(function () {}); // => [object Function]
Object.prototype.toString.call(/test/i); // => [object RegExp]
Object.prototype.toString.call(true); // => [object Boolean]
Object.prototype.toString.call(null); // => [object Null]
Object.prototype.toString.call(); // => [object Undefined]
- constructor,他能准确判断数据类型,但是可以轻易被改变
console.log((2).constructor === Number); // true
console.log((true).constructor === Boolean); // true
console.log(('str').constructor === String); // true
console.log(([]).constructor === Array); // true
console.log((function() {}).constructor === Function); // true
console.log(({}).constructor === Object); // true
function Fn(){};
Fn.prototype = new Array();
//通过constructor改变:
var f = new Fn();
console.log(f.constructor === Fn); // false
console.log(f.constructor === Array); // true
14.创建对象
- 对象字面量
- Object.create或 new Object
new 的实现:
1.创建一个空对象obj;
2.将新创建的对象的隐式原型指向其构造函数的显式原型;
3.将this绑定到obj实例上;
4.无返回值则返回obj,返回值为新对象则返回该对象。
function new() {
let obj = {}
let con = [].shift.call(arguments)
obj._proto_ = con.prototype
let result = con.apply(obj,arguments)
return result instanceof Obj ? result : Object
}
- 构造函数
function Person(name, age){
this.name = name;
this.age = age;
this.say = function() {
console.log(this.name + ' ' + this.age);
}
}
var person1 = new Person('Tom', 18);
var person2 = new Person('Harry', 28);
15.继承
- 原型继承
实现:把父类的原型放到子类的原型链上。原型继承的本质是复制,重写原型对象- 组合继承
- 寄生继承
- Class 继承
参考:zhuanlan.zhihu.com/p/25578222
blog.csdn.net/sinat_36263…
16.var let const
- var 存在变量提升(为了解决函数间互相调用),会被自动挂载到window上,变量可以在声明前使用,变量允许重名。函数提升优先于变量提升。
- let const 不存在变量提升,存在暂时性死区,不能在声明前使用,不会被挂载window上,即使声明在全局。存在块级作用域。
- const 声明变量在声明时就必须复制。
17.模块化
将一个复杂的程序依据一定的规则(规范)封装成几个块(文件), 并进行组合在一起
块的内部数据与实现是私有的, 只是向外部暴露一些接口(方法)与外部其它模块通信
- 模块化的作用:
1、解决命名冲突,避免全局变量被污染
2、便于代码编写和维护
3.方便依赖管理
- AMD和CMD:AMD 推崇依赖前置、提前执行,CMD推崇依赖就近、延迟执行。
- Common.js:用module.exports定义当前模块对外输出的接口(不推荐直接用exports),用require加载模块。模块输出一个值的拷贝,一旦输出,之后模块内部的变化不会影响输出的值。common.js在require之后就加载整个模块。
- ES6模块化:用export、export default 对外输出接口,用import加载模块。模块输出一个值的引用,模块内的值发生改变,import的值也会改变。在import后只读引用,到真正被加载时才到模块里去取值。
18.对象方法
19.字符串方法
20.事件机制
- 事件捕获:
- 事件冒泡:
绑定事件(addEventListener)的第三个参数(useCapture)默认为false,即注册事件为冒泡事件,设置为true 则注册事件为捕获。
target和currentTarget
事件委托:
<div>
<ul>
<li>1</li>
<li>2</li>
<li>3</li>
<li>4</li>
<li>5</li>
</ul>
</div>
const click = document.querySelector("ul").addEventListener("click",function(e){
console.log("asdasda");
},true);
stopPropagation:阻止事件冒泡
stopImmediatePropagation:阻止所有事件
21.js垃圾回收
标记-清除 算法
参考:segmentfault.com/a/119000001…
22.那些常见的手写系列
- 深浅拷贝
- call、apply、bind
- 模板编译
const template = `
<div class="play" name="{{name}}">{{ value }}</div>
`
const data = {
name: 'Mary',
age: 18
}
const compiler = (str, data) => {
const reg = /\{\{(.*?)\}\}/g
return str.replace(reg, (patten, g1) => {
const key = g1.trim()
return data[key] ? data[key] : ''
})
}
const content = compiler(template, data)
console.log(content)
// <div class="play" name="Mary">18</div>
- new 的实现
- 防抖、节流
- 数组扁平化
- 数组去重