寒冬期前端准备总结---JS篇

⚠️⚠️传送门⚠️⚠️

[寒冬期前端准备总结---JS篇]
寒冬期前端准备总结---浏览器篇
寒冬期前端准备总结---服务器和网络篇
寒冬期前端准备总结---CSS篇
寒冬期前端准备总结---框架篇
寒冬期前端准备总结---算法篇

  • 原型/原型链/构造函数/实例/继承
  • 所有对象都有隐式原型属性proto,所有构造函数都有自己的原型prototype,且隐式原型_proto指向创造这个对象的构造函数的原型,构造函数的原型中有构造器constructor,构造器指向构造函数本身。通过隐式原型可以向上查找属性和方向供对象使用。
image.png

上图原型链结构:p对象的原型链 -> Person.prototype -> Object.prototype -> null

image.png
  • ES5、ES6的继承区别
ES5
   原型链继承,通过原型链之间的指向进行委托,prototype属性进行关联继承
   构造函数继承,通过在子类内部调用父类,使用apply()和call()方法在新创建的对象上获取父类的方法,先有自己的this对象,再扩充this的属性

ES6
  class类实现继承,创建的子类使用extends直接继承父类,并在构造器中调用super(),子类自己的this对象必须先通过父类的构造函数完成塑造,如果不调用super方法,子类就得不到this对象,先拿到父类的this,再有自己的this

* ES5原型链继承 :子构造函数的原型指向父构造函数的实例。

function Person(name) {

  this.name = name

  this.skills = ['eat', 'sleep']

}

Person.prototype.say = () => { console.log('hi'); }

function Boss() {}

Boss.prototype = new Person()

let Han = new Boss()// 父级引用共享

* ES5构造函数继承:父构造函数把执行对象赋给子构造函数的实例对象后执行自身

// 可以向父级传递参数

function Person(name) {

  this.name = name

  this.skills = ['eat', 'sleep']

}

Person.prototype.say = ()=> {console.log('hi')}

function Boss(name) {

  Person.call(this, name)

}

let Han = new Boss('Han')// 可以向父级传参,父级引用不共享
  • 用new创建一个对象的过程
1 let obj = {} // obj = new Object()

2 obj.__proto__ = Object.prototype // 对象的隐式属性指向构造函数的原型prototype,

3 Object.call(obj) // 调用上层的构造函数,使用call将this指向对象实例

* 如果在一个引用类型的对象上找不到某一个属性,会往她的构造函数的prototype里面查找

  console.log(obj.__proto__ === Object.prototype);

  __proto__:隐式原型  prototype:显示原型

  Object.prototype.__proto__ == null;

  Object.__proto__ == Function.prototype;

  Function.prototype.__proto__ == Object.prototype;

**__proto__是形成他们之间关联(原型链)的主要属性**

🌰**原型链分析数组对象的方法🌰**

1 let arr  = [];// 为let arr = new Array();的语法糖

2 arr.push方法在arr对象里并不存在,查找隐式原型__proto__;其中arr.__proto__ = Array.prototype;

3 也就是说push方法存在于Array的显式原型prototype上
  • 数据类型
基本数据类型存储在栈中,复杂类型变量栈中存储的是指向堆的引用地址

基本类型按值访问;复杂类型是按引用访问

赋值:基础类型是值的副本,复杂类型是引用地址
  • 数据结构
* 数组:插入快、删除慢

* 队列:先进先出

* 栈:后进先出

* 链表:插入删除快(指针),查询慢

* 二叉树:插入删除查询快
  • 类型判断
* typeof判断基本数据类型:number、string、boolean、object、undefined,对于array、function、null、object均返回object

* Object.prototype.toString.call方法用于判断某个对象属于哪种内置类型,常用于判断浏览器内置对象

* instanceof 判断对象是不是某类的实例 person instanceof Person,实现机制时判断对象的原型链中能不能找到类型的prototype

* 判断一个类为另一个类的子类:子类的原型的所有属性在父类上是否存在;duck-typing弱化对象的类型,强化了对象的功能

* 另外ES5的Array.isArray方法可以用来判断数组

    // 兼容数组判断

    if(!Array.isArray) {

        Array.isArray = function(arg) {

            return Object.prototype.toString.call(arg) === '[object Array]';

        };

    }

* Set数组去重不包括NaN和{} : NaN === NaN (false)   ||  {} === {} (false)
  • 判断一个对象是否为空
* 将对象stringify为字符串,判断“{}”

* for in判断对象有没有key;for in 遍历自身和原型上的所有可枚举属性

* **hasOwnProperty方法会屏蔽掉原型上的属性**(console.log(arr.hasOwnProperty('push')) // false)

* Object.getOwnPropertyNames判断属性数组长度

* Object.keys判断属性数组长度(ES6)
  • 对象的键名
* 只能是字符串和Symbol类型,每一个Symbol类型的数据都是唯一的

* 其他类型的键名会被强制转化为字符串

* 对象通过toString方法转为字符串 // {a: 1} toString后为 [object object]
  • 深拷贝JSON.stringify的存在问题
* 可以实现基本类型数据的深拷贝

  1 忽略不能被文本化的属性,例如function

  2 循环引用会丢失,被忽略返回空对象

  3 Symbol和undefined等不能用JSON表示的属性会丢失,数组中不能被stringify的元素会被用null填充(JSON是一个文本格式,不会对特殊的语言做处理)

* 重写对象原型prototype上的toJSON方法实现自定义的JSON.stringify的返回结果

* JSON.stringify(value, replacer?, space?)

  第二参数replacer:表示需要stringify的属性名称数组或一个过滤属性的函数,且嵌套的属性也会被过滤掉

  第三参数space:用于格式化输出结果
  • 箭头函数和普通函数的区别
* 函数体内的this对象,是定义箭头函数时所在的对象的this,不是调用时对象的this

* 不可以使用arguments

* 不可以使用yield命令,不能用于Generator函数

* 不可以使用new实例化,无this,无prototype
  • 操作符==会进行隐式的类型转换,toString方法的重写
var a = {

    i: 1,

    toString() {

        return a.i++;

    }

}

if(a == 1 && a == 2 && a == 3) {

    console.log(true);

}
  • 连续赋值题
var a = {n: 1};

var b = a;

a.x = a = {n: 2};

a;// {n: 2}

b;// {n: 1, x: {n: 2}}

* . 运算符的优先级高于 = 。先执行a.x,此时内存中的{n: 1}变成{n:1,x:undefined};此时b.x和a.x都为x:undefined

* 赋值操作从右往左。先执行a = {n: 2};此时a指向新的内存地址,a为{n: 2};然后将这个返回值赋值给a.x,a.x为之前的{n:1,x:undefined}中的x对象,所以{n:1,x:undefined}变成了{n:1,x: {n: 2}},此时b指向这个内存地址
  • call、apply、bind
* 都是改变this指向的,第一个参数为null,表示this指向全局window/global

* call后面参数一个一个的传、apply后面的参数放在数组中传

* bind后面参数一个一个传,但是返回一个新的函数,调用后才会执行

**callee** arguments.callee:arguments对象的属性,用来指向当前执行函数

**caller** arguments.callee.caller:function对象的属性,返回function的引用
- a.b.c.d的性能优于a[‘b'][‘c'][‘d’]:[]需要考虑变量的情况,且dot在编译解析的时候比中括号速度快

- ES6转ES5的原理:babel工具先将ES6代码解析成抽象语法树,修改抽象语法树,再将修改后的抽象语法树转化成ES5的代码

- setTimeout的第三个参数会作为回调方法的第一个参数传入

- var存在变量提升(先使用后声明);let、const没有变量提升(先声明后使用) 但是有暂时死区,不在全局对象(window/global)上,只存在于一个script块级作用域上

- 类数组的push方法是根据类数组的length来决定从何处插入数据的,length不存在或者不能转为数字时,默认length为0

- 数组扁平化 arr.flat(Infinity)数组  |  arr.toString()逗号分隔字符串
  • 进程和线程
* JS是单线程执行的,线程是CPU调度的最小单位,进程是CPU资源分配的最小单位

* 一个进程可以由一个或多个线程组成,且一个进程的内存空间是共享的

* 浏览器内核为多线程,常驻线程包括:GUI渲染线程、JS引擎线程、事件触发线程、定时器线程和异步http请求线程
  • 事件冒泡、事件捕获、事件委托
* 事件捕获:从window层逐步向深层的节点传播事件

* 事件冒泡:从深层节点逐步向外层传播事件

* 外层节点先事件捕获(true)再事件冒泡(false);到目标节点时根据事件绑定的先后顺便执行事件

* 阻止事件传播:e.stopPropagation

* addEventListener方法的第二个参数,false表示执行冒泡、true表示执行捕捉,默认为false

* e.target表示触发事件的目标元素;e.currentTarget表示绑定事件的元素
  • js内存泄漏和垃圾回收机制
内存泄漏的情景

* 全部变量引起的泄漏

* 闭包引起的泄漏

* dom删除或清空时,事件未清空引起的泄漏

垃圾回收机制

* 计数法:变量被引用的次数计数,但是循环引用时无法被回收

* 标记清除:当变量进入执行环境时标记使用,结束执行环境时标记回收,从根节点标记被使用的变量,清除未被使用的变量
  • 函数
// 函数表达式

var a = function() {}

// 函数声明

function b() {}

* 函数表达式不同于函数声明,函数表达式中的函数名只在该函数内部有效,并且函数名绑定为常量绑定,对常量赋值在严格模式下会报错

* 函数声明优先于变量声明,**函数声明会在执行之前被解析 存在覆写情况(完全提升声明前可以调用);变量声明是在执行过程中解析函数**

* 函数表达式包括匿名函数和具名函数

* 函数声明会被提升,函数表达式不会

函数表达式👇

var a = 10;

// 'use strict'

(function a() {

    a =20; // 具名函数a,不能被赋值

    console.log(a); // a为函数a,严格模式下报错(Uncaught TypeError: Assignment to constant variable.)

})()

var a = 10;

// 'use strict'

(function a(a) {

    a =20;

    console.log(a); // 20

})(a)
  • 模块化
* AMD:异步加载模块,使用define导出模块,require([module])引入模块,提前执行且不能按需加载

* CMD:延迟执行、按需加载、依赖就近,跟AMD的语法相同

* CommonJS:同步加载模块,主要用于node环境中,运行时加载(只会在第一次加载时运行,之后都是缓存,加载的对象在脚本运行完成时才生成),是**值的拷贝**

* ES6:import、export 按需编译时加载,需要babel转码,是**对值的引用**,静态编译(webpack同步、SystemJS异步)动态引用没有缓存值,在代码编译阶段就会生成
  • Node和浏览器的事件循环
微任务:Promise.then、await后面的脚本

宏任务:setTimeout、setInterval、script、Promise的构造函数、async的函数体

* Node端,微任务在事件循环的各个阶段之间执行,宏任务有六个阶段,如下图

* 浏览器端,微任务的任务队列都是在每个宏任务执行完成之后执行的

node11 以前:

* 执行完一个阶段的所有任务

* 执行完nextTick队列里面的内容

* 然后执行完微任务队列的内容

node11以后:和浏览器行为统一,每执行一个宏任务就执行完微任务队列

在node中process.nextTick()不属于事件循环的一部分,无论时间循环的当前阶段如何,都将在当前操作完成后处理nextTick
image.png
  • 函数节流和函数防抖
函数防抖:事件被触发n秒后再执行回调,如果在这n秒内又被触发,则重新计时

函数节流:规定一个单位时间内,只能有一次触发事件的回调的函数被执行,如果单位时间内事件被多次触发,只能有一次事件生效

* 防抖应用场景:适合多次事件一次响应

    按钮加函数防抖,防止表单的多次提交

    输入框输入校验,用函数防抖有效减少校验的请求次数

    判断页面是否滚到底部

🌰

var timer = null

element.input = function () {

    clearTimeout(timer)

    timer = setTimeout(function () {

        // DO SOMTHING...

    }, 1000)

}

* 节流的应用场景:适合大量事件按时间做平均分配触发

    页面的刷新

    DOM元素的拖拽

    Canvas的画笔功能
  • 高阶函数、函数柯里化
* 高阶函数:将函数作为返回值,形成高阶函数  add(1)(2)(3):

🌰

function add(a) {

    function sum(b) { // 使用闭包

        a = a + b;

        return sum;

    }

    // 重写toString方法,用于返回变量a的值,console.log(a(1)(2)(3))

    sum.toString = function() {

        return a;

    }

    return sum;

}

* 柯里化:把多个参数的函数转为返回单一参数的新函数的形式

🌰

// 不限形参的个数 优化版柯里化函数

function add() {

    var args = Array.prototype.slice.call(arguments);

    var fn = function() {

        var newArgs = args.concat(Array.prototype.slice.call(arguments));

        return multi.apply(this, newArgs);

    }

    fn.toString = function() {

        return args.reduce(function(a, b) { return a + b; })

    }

    return fn;

}

* 柯里化的实现方式:用闭包把传入参数保存起来,当传入参数的数量足够执行函数时,就开始执行函数
    延迟计算
    动态创建函数
    参数复用
  • JS数组方法总结
* includes:判断数组是否存在某个元素;返回true/false

* forEach:遍历数组,该方法不会改变原数组,且没有返回值

* map:遍历数组,不会改变原数组,返回新数组,函数没有显示返回值时,新数组各项为undefined

* find:根据检索条件,查找数组中第一个满足条件的元素,没有时返回undefined

* findIndex:同上,返回元素下标

* filter:根据条件,过滤返回获得的新数组,不改变原数组

* push:数组末尾添加一个元素,返回数组新长度

* pop:删除数组最后一个元素,并返回该元素

* shift:删除数组第一个元素,并返回该元素

* unshift:数组开头添加一个元素,返回数组新长度

* concat:数组拼接,不会改变原数组,返回拼接后的新数组

* reverse:数组元素倒置,**改变原数组**,且返回新数组

* sort:数组排序,返回排序后的数组,且改变原数组,不传递函数时,使用toString后比较大小(a,b)=> a - b 生序

* join:已分隔符将原数组元素连接生成字符串

* split:字符串分隔为数组,第二参数为分隔符

* every:判断数组所有项是否条件,返回true/false

* some:判断数组任一项是否条件,返回true/false

* indexOf/lastIndexOf:查找元素,第一参数为查找元素,第二参数为开始位置(正向查找/反向查找),返回下标

* slice(start, end):数组截取,返回区间元素数组,**不改变原数组**

* splice(start, howmany, item1, item2):数组截取,删除指定数目的项,添加新的项,返回删除的元素,**改变原数组**

* reduce:为数组中的每一个元素依次执行回调;第一参数:初始值/上一次回调的返回值;第二参数:当前元素;第三参数:当前索引;第四参数:调用reduce的数组 arr.reduce((total, num) => total + num);
  • 字符串转数字~~、+、Math.floor、parseInt
* Math.floor 负数时,向下取整 但是只能转标准格式的字符串,效率高于parseInt

* ~~ 负数时,向上取整,只能转标准格式的字符串,非标准结果为0

* + 将字符串转数字,不能转为整数,只能转标准格式的字符串(+"0" === false)

* parseInt效率最低,但是可以转非标准格式的字符串, 第二参数为转化的进制,不设置时会根据string来判断数字的基数0x转化为16进制
  • 设计模式
* 发布订阅(观察者)
  当一个对象的状态发生改变时,所有依赖于它的对象都将得到通知
* 工厂
  提供一个创建一系列相关或相互依赖对象的接口,而无需指定他们具体的类
* 单例
  保证一个类仅有一个实例,并提供一个访问它的全局访问点
  应用:全局缓存,浏览器中的window对象等
* 代理
  把对一个对象的访问, 交给另一个代理对象来操作
  • 前端性能优化
- 减少HTTP请求:合并文件
- 减少DNS查询:DNS缓存
- 非必须组件的延迟加载
- 未来组件的预加载
- 减少DOM元素的数量,以及减少DOM操作
- 将资源放在不同域名下,增加域可以提供并行下载量
- 减少使用CSS表达式
- 脚本放在页面底部,防止脚本阻塞
- 压缩js和css
- 图片优化(图片适配、雪碧图等等)
- 使用CDN,
- 添加缓存相关的响应头
- 配置ETag
- 资源js、css压缩
- 资源协商缓存,
- 按需引用资源
  • 图片优化
选择合适的图片格式
* BMP:没有压缩像素格式
* JPG:有损压缩,没有透明效果,可以在保存时,可以选择压缩比例控制图片文件大小(60-80最佳);
      JPG的“基线”从上到下加载,“连续”从模糊到清晰
* PNG:无损压缩,有透明效果,png8颜色值256色
* GIF:多帧动图,有透明度
* webp:google开发的,有损压缩,体积降低更多
* 总结:照片使用JPG,图标使用PNG,动画使用GIF

降低体积
* 压缩图片大小,在线压缩工具

响应式加载
* css的@media决定加载背景图片的大小
* h5的img属性,srcset为不同宽度的图片设置图片资源地址;sizes根据视图宽度决定图片渲染的宽度

🌰
<img class="img" src="imgbg-320.jpg”

    srcset="imgbg-320.jpg 320w, imgbg-360.jpg 360w, imgbg-480px.jpg 480w”

    sizes="(max-width: 480px) 480px, 320px”>

* picture标签根据视图大小加载不同资源,暂时未被浏览器兼容

减少HTTP的网络请求

* css sprites(背景精灵图/雪碧图):将多个小的图标放在同一张大图中,在css里面加载一次,每次使用时借助background-position、width、height使用数字精确的定位特定的背景大图位置
* 使用css来绘制一下图形图标,例如三角形,代替http请求
* svg矢量图使用XML格式定义图形,不会在缩放时质量改变
* h5 的canvas标签绘制图片
* base64,将一些小图标(2KB左右)转化成base64嵌入到页面html或者css中

图片延迟加载和图片预加载

* 延迟加载,当页面滚动到一定位置时,在加载新图片
* 预加载,对与当前页面不同域的服务器进行DNS Prefetch预加载(link标签),将静态资源放在不同域名的服务器上实现预加载

字体图库iconfont
* 在网站上定制图标库,下载包后,可以引入css文件,在i标签中写入对应的类名
  • set、map、WeakSet和WeakMap
* set用于数据重组,map用于数据存储
* set是成员唯一且无序的类数组结构,注意对象引用;WeakSet只能存储引用对象,不能存放值,且不能被遍历,对象可能会被垃圾回收掉;
* map以[key, value]的形式存储数据,构造函数的参数:任何具有Iterator接口、且每个成员都是一个双元素的数组的数据结构都可以;键名不限于字符串
    let map = new Map([[‘a’, 1], [‘b’, 2]]);
    map.set(‘c’,’3’);
* WeakMap只接受对象作为键名,且键名为弱引用,会被垃圾回收,不能遍历
  • Proxy
* 在目标对象之前架设一层“拦截”,外界对该对象的访问,都必须先通过这层拦截,因此提供了一种机制,可以对外界的访问进行过滤和改写
* 类似于Object.defineProperty,优于Object.defineProperty,可拦截13种方法 https://es6.ruanyifeng.com/#docs/proxy

Object.defineProperty是对一个对象的一个属性劫持,需要遍历整个对象的属性,proxy是对整个对象的劫持
🌰
let targetObj = {a: 1};

let obj = new Proxy(targetObj, {

  get: function (target, key, receiver) {

    console.log(`getting ${key}!`);

    return ‘hhh';

  },

  set: function (target, key, value, receiver) {

    console.log(`setting ${key}!`);

    return Reflect.set(target, key, value, receiver);

  }

});

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