JS记录

JavaScript

原文链接https://juejin.im/post/5c64d15d6fb9a049d37f9c20

1. 原型/构造函数/实例

  • 原型:一个简单对象,用于实现对象的继承属性

  • 构造函数:通过 new 来新建一个对象的函数。

  • 实例:通过构造函数和 new 创建出来的对象。

2. 原型链

  • 原型链是由原型组成。是一个用来实现继承和共享属性的有限的对象链。
  • 属性查找机制:当查找对象的属性是,如果实例对象自身不存在该属性,则沿着原型链往上一级查找,直至最顶级的 Object.prototype。如果没找到,则输出 undefined
  • 属性修改机制:只会修改实例对象本身的属性,不存在则添加,存在则修改。

3. 执行上下文

执行上下文可以简单理解成一个对象:

  • 它包含三部分:

    1. 变量对象(VO)
    2. 作用域链(词法作用域)
    3. this 指向
  • 它的类型

    1. 全局执行上下文
    2. 函数执行上下文
    3. eval 执行上下文
  • 代码执行过程

    1. 创建 全局上下文(global EC)
    2. 全局执行上下文(caller)逐行 自上而下 执行。遇到函数时,函数执行上下文(callee) 被 push到执行栈顶层。
    3. 函数执行上下文被激活,成为 active EC,开始执行函数和的代码,caller 被挂起。
    4. 函数执行完后,callee 被 pop移除执行栈,控制全交换全局上下文(caller),继续执行。

3.1 变量对象

变量对象:是执行上下文中的一部分,可以抽象为一种数据作用域,其实也可以理解为就是一个简单的对象,它储存着该执行上下文中的所有 变量和函数声明(不包括函数表达式)。

活动对象(AO):当变量对象所处的上下文为 active EC 时,称为活动对象。

3.2 作用域

作用域 可以理解为该上下文声明的 变量和声明的作用范围。可分为 块级作用域函数作用域

特性:

  • 提前声明:一个声明在函数体内都是可见的,函数优先于变量
  • 费匿名自执行函数,函数变量为 只读状态,无法修改。

4. 闭包

闭包属于一种特殊的作用域,成为静态作用域

它可以理解为:父函数被销毁的情况下,返回出的子函数中仍然保留着父级作用域的变量和作用域,因此可以继续访问到父级的变量对象,这样的函数成为闭包。

  • 闭包会产生的问题:
    多个子函数都同时指向父级,父级的状态是完全共享的。一次当父级的变量对象被修改是,所有子函数都受到影响。

5. script 引入方式

  • html 静态 <script> 引入
  • js 动态插入 <script>
  • <script defer>: 延迟加载,元素解析完成后执行
  • <script async>: 异步加载,但执行时会阻塞元素渲染。

6. 对象拷贝

  • 浅拷贝:以赋值的形式拷贝引用对象,仍指向同一个地址,修改时原对象也会收到影响
  1. Object.assign
  2. 展开运算符(...)
  • 深拷贝:完全拷贝一个新对象,修改是原对象不再受到任何影响

  • JSON,parse(JSON.stringify(obj))
    具有循环引用时报错。
    当值为函数,undefined,或 symbol时,无法拷贝

  • 递归进行逐一赋值

7. new 运算符的执行过程

  1. 生成一个新对象
  2. 链接到原型 obj.__proto__ = Con.prototype
  3. 使用 apply 进行 this 绑定。
  4. 返回新对象(如果构造函数有自己 return 时,则返回改值)

8. instanceof 原理

instanceof 主要的实现原理就是只要右边变量的 prototype 在左边变量的原型链上即可。因此,instanceof 在查找的过程中会遍历左边变量的原型链,直到找到右边变量的 prototype,如果查找失败,则会返回 false。

function instanceof(left, right) {
    const rightVal = right.prototype
    const leftVal = left.__proto__
    // 若找不到就到一直循环到父类型或祖类型
    while(true) {
        if (leftVal === null) {
            return false
        }
        if (leftVal === rightVal) {
            return true
        }
        leftVal = leftVal.__proto__ // 获取祖类型的__proto__
    }
}

9. 代码复用

当代码需要重复书写时,就要考虑如何复用,一般有以下几种方式:

  • 函数封装
  • 继承extend

10. 继承

在 js 中,继承通常指的是原型链继承,指的是通过原型链继承原型上的属性或者方法。

  • 最优化:神杯模式
var inherit = (
  function(c,p){
    var F = function(){}
    return function(c,p){
      F.prototype = p.prototype;
      c.prototype = new F();
      c.xxx = p.prototype;
      c.prototype.constructor = c;
    }
  }
)()
  • 使用 ES6 的语法糖class/extends

11. 类型转换

  • 减(-)乘(*)取商(/)取模(%):一律转换成数值后计算
  • 加(+):
    数字 + 字符串 = 字符串
    数字 + 对象,优先调用对象 valueOf > toString
    数字 + boolean/null -> 数字
    数字 + undefined -> NaN
  • [1].toString === '1'
  • {}.toString() === '[object object]'
  • NaN !== Nan +undefined 为 NaN

12. 类型判断

判断 Target 的类型,单单使用 typeof 无法满足,这其实不是 bug,本质因为 JS 的万物皆对象导致的。因此需要区分对待:

  • 基本类型(null):使用 String(null)
  • 基本类型(string/number/boolean/undefined) + function:直接使用 typeof即可
  • 其余引用类型(Array/Date/RegExp/Error):调用 toString后根据 [object xxx] 进行判断
let class2Type = {};
'Array Date RegExp Object Error'.split(' ').forEach(e => {class2Type[`[object ${e}]`] = e.toLowerCase()})
function type(obj) {
    if (obj == null) return String(obj)
    return typeof obj === 'object' ? class2type[ Object.prototype.toString.call(obj) ] || 'object' : typeof obj
}

13. 模块化

在浏览器中使用 ES6 的模块化支持,在 Nodejs 中使用 commonjs 的模块化支持。

  • 分类

    1. es6:import/expoer
    2. commonjs: require/module.exports/exports
    3. amd: require/define
  • requireimport的区别
    1.require支持动态导入import不支持,正在提案(babel下可支持)

    1. require同步导入,import属于异步导入。
    2. require值拷贝,到处变化不会影响导入值;import 指向内存地址,导入值会随导出值而变化。

14. 节流和防抖

节流和防抖函数是一种常用的 高频触发优化方式,对性能提高有较大帮助。

  • 防抖(debounce):将多次高频操作优化为只在最后一次执行,使用场景:用户输入,只需在输入完成后做一次输入校验即可。
function debounce(fn, wait, immediate) {
    let timer = null

    return function() {
        let args = arguments
        let context = this

        if (immediate && !timer) {
            fn.apply(context, args)
        }

        if (timer) clearTimeout(timer)
        timer = setTimeout(() => {
            fn.apply(context, args)
        }, wait)
    }
}
  • 节流(throttle):每隔一段时间后执行一次,将高频操作优化生低频操作。使用场景:滚动条时间或者 resize 事件,通常每隔 100~500 ms 执行一次即可。
function throttle(fn, wait, immediate) {
    let timer = null
    let callNow = immediate
    
    return function() {
        let context = this,
            args = arguments

        if (callNow) {
            fn.apply(context, args)
            callNow = false
        }

        if (!timer) {
            timer = setTimeout(() => {
                fn.apply(context, args)
                timer = null
            }, wait)
        }
    }
}

15. 函数执行改变 this

由于 JS 的设计原理:在函数中,可以引用运行环境中的变量。因此就需要一个机制来让我们可以在函数体内部获取当前的运行环境,这就是 this
因此要明白 this指向,其实就是要搞清楚函数的运行环境。换句话说,就是谁调用了函数。例如:

  • obj.fn(),便是 obj 调用了函数,即函数中的 this === obj
  • fn(),这里可以看成 window.fn(),因此 this === window

但这种机制并不是完全满足业务需求,因此提供了三种方式可以手动修改 this的指向:

  • call: fn.call(target,1,2)
  • apply: fn.apply(target, [1,2])
  • bind: fn.bind(target)(1,2)

16. ES6/ES7

由于 Babel 的强大和普及,现在 ES6/ES7 基本已经是现代开发的必备了。通过新的语法糖,能让代码整体更为简洁和易读。

  • 声明:
    1. let/const: 块级作用域,不存在变量声明提升,暂时性死去,不允许重复声明
    2. const: 声明常亮,无法修改
  • 解构赋值
  • class/extends: 类声明与继承
  • Set/Map:新的数据解构
  • 异步解决方案:
    1. promise 的实现和使用
    2. generator:
    • yield: 暂停代码
    • next(): 继续执行代码
    1. await/async: 是 generator 的语法糖,babel 中式基于 promise 实现。

17. AST(抽象语法树)

抽象语法树(Abstract Syntax Tree)是将代码逐行字母解析成 树状对象的形式。这是语言之间的转换,代码语法检测,代码风格检测,代码格式化,代码高亮,代码错误提示,代码自动补全等等的基础。例如:
[图片上传失败...(image-e38f13-1581929094976)]

18. babel 编译原理

  • babylon 将 ES6/ES7 代码解析成 AST
  • babel-traverse 对 AST 进行遍历转义,得到新的 AST
  • 新 AST 通过 babel-generator 转换成 ES5

19. 函数柯里化

在一个函数中,首先填充几个参数,然后再返回一个新的函数的技术,成为函数的柯里化。通常可用于在不侵入函数的前提下,为函数预设通用参数,供多次重复使用。

const add = function add(x) {
    return function (y) {
        return x + y
    }
}

const add1 = add(1)

add1(2) === 3
add1(20) === 21
  1. 数组(array)
  • map: 遍历数组,返回回调返回值组成的新数组
  • forEach: 无法break,可以用try/catch中throw new Error来停止
  • 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 为当前值(从第二项开始)

数组乱序:

var arr = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
arr.sort(function () {
    return Math.random() - 0.5;
});

数组拆解: flat: [1,[2,3]] --> [1, 2, 3]

Array.prototype.flat = function() {
    return this.toString().split(',').map(item => +item )
}
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 194,088评论 5 459
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 81,715评论 2 371
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 141,361评论 0 319
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,099评论 1 263
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 60,987评论 4 355
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,063评论 1 272
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,486评论 3 381
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,175评论 0 253
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,440评论 1 290
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,518评论 2 309
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,305评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,190评论 3 312
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,550评论 3 298
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 28,880评论 0 17
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,152评论 1 250
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,451评论 2 341
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,637评论 2 335

推荐阅读更多精彩内容