JS

517 阅读12分钟

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 '' NANfalse,其他都为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

参考:juejin.cn/post/684490…

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

参考:juejin.cn/post/684490…

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.继承

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 的实现
  • 防抖、节流
  • 数组扁平化
  • 数组去重