您当前的位置: 首页 >  Java

暂无认证

  • 0浏览

    0关注

    92582博文

    0收益

  • 0浏览

    0点赞

    0打赏

    0留言

私信
关注
热门博文

JavaScript-big

发布时间:2021-04-07 23:26:30 ,浏览量:0

文章目录
  • 01、JavaScript数据类型及存储方式
  • 02、var、let和const的区别
  • 03、null和undefined的区别
  • 04、防抖debounce
  • 05、节流throttle
  • 06、克隆、拷贝
  • 07、作用域和作用域链
  • 08、闭包
  • 09、原型和原型链
  • 10、new操作符
  • 11、两等(==)和非(!)的优先级比较
  • 12、数据类型转换
  • 13、检测数据类型的方法
  • 14、检测一个对象是不是数组
  • 15、this
  • 16、ES6及往后版本的新特性
  • 17、箭头函数与普通函数有什么区别
  • 18、字符串常用方法
  • 19、数组常用方法
  • 20、indexOf和findIndex的区别
  • 21、双等与三等有什么区别
  • 22、事件委托、代理
  • 23、JavaScript之继承
  • 18、图片懒加载
  • 20、JavaScript实现内置方法
  • 21、函数柯理化
  • 22、任务(事件)队列和事件循环
  • 23、正则表达式
  • 24、实现浏览器内多个标签页(.html 页面)之间的通信
  • 25、面向对象(OOP)
  • 17、JavaScript的内置对象
  • 26、严格模式
  • 27、变量的提升
  • 30、事件捕获和事件冒泡
  • 31、设计模式
  • 32、promise、async和await
  • 33、垃圾回收机制
  • 34、DOM
  • 35、BOM
  • 36、ajax
  • 37、观察者模式
  • 38、发布订阅模式
  • 41、原型链的prototype和__proto__的区别
  • ------------------------------zd------------------------------
  • 1、回调函数
  • 2、变量提升
  • 3、函数作用域 (scopes) 和函数作用域链
  • 4、闭包
  • 5、统计字符串
  • 6、this
  • 7、深浅克隆
  • 8、构造函数(constructor)
  • 9、包装类型
  • 10、内置类
  • 11、重写
  • 12、面向对象
  • 13、访问器属性
01、JavaScript数据类型及存储方式

数据类型

JavaScript共有8种数据类型。 7种基本数据类型:Null、Undefined、Boolean、Number、String、Symbol(ES6新增,表示独一无二的值)和BigInt(ES10新增)。 1种引用数据类型:Object。Object里面包含Object、Array、Function、Date、RegExp等。 总结:JavaScript不支持任何创建自定义类型的机制,而所有值最终都将是上述8种数据类型之一。

存储方式

1、原始数据类型直接存储在栈(stack)中,占据空间小且大小固定,属于被频繁使用的数据,所以放入栈中存储。 2、引用数据类型同时存储在栈(stack)和堆(heap)中,占据空间大,且大小不固定。引用数据类型在栈中存储了指针,该指针指向堆中该实体的起始地址。当解释器寻找引用值时,会首先检索其在栈中的地址,取得地址后从堆中获得实体。

原文链接 1、原文

02、var、let和const的区别

区别

1、var声明的变量会挂载在window上,而let和const声明的变量不会。 2、var声明变量存在变量提升,let和const不存在变量提升。 3、let和const声明形成块作用域。 4、同一作用域下var可以声明同名变量,而let和const不可以。

相关链接 1、原文 2、掘金-原文

03、null和undefined的区别

区别

unll:用来初始化一个变量,这个变量可能赋值为一个基本类型值或者引用类型值。undefined:声明一个变量,但是没有赋值。 1、null表示一个变量被人为的设置为空对象, 而不是原始状态。 2、undefined表示一个变量最原始的自然状态值。 3、在实际使用过程中,为了保证变量所代表的语义,不要对一个变量赋值为undefined,当需要释放一个对象时,直接赋值为null即可。

原文链接 1、原文

04、防抖debounce

多种不同的解释或定义

1、防抖是指短时间内多次触发,最终在停止触发后的某个指定时间执行一次函数,只执行一次。 2、防抖是指触发事件n秒后才执行函数,如果在n秒内又触发事件,则会重新计算函数执行时间。 3、防抖是n秒内重复的触发会导致重新计时,直到n秒内没有重复触发函数才会执行。

原文链接 1、原文

05、节流throttle

多种不同的解释或定义

1、短时间内多次触发,即使触发仍在继续也可以根据指定。时间触发一次函数,至少执行一次。 2、节流是指连续触发事件,但是在n秒中只执行一次函数。节流会稀释函数的执行频率。 3、节流是n秒内只会执行第一次触发的函数,重复的触发无效。

原理

主要的原理就是在闭包内设置一个标记,在限定的时间内这个flag设置为true,函数再次点击则让执行,setTimeout函数执行以后将flag设置为flase,就可以继续执行 。

相关原文链接 1、原文 2、原文

06、克隆、拷贝

定义

1、浅拷贝只是增加了一个指针指向已存在的内存地址。 2、深拷贝是增加了一个指针并且申请了一个新的内存,使这个增加的指针指向这个新的内存。

原文链接 1、浅浅克隆和浅克隆 2、深克隆 3、深克隆

07、作用域和作用域链

作用域定义

作用域是指程序中定义变量的区域,它决定了当前执行代码对变量的访问权限。

作用域链的定义

查找变量的时候,先从当前上下文的变量对象中查找,如果没有找到,就向父级执行上下文的变量对象中查找,一直找到全局上下文的变量对象,如果全局上下文对象中也没有找到变量,则返回undefined。这样由多个执行上下文的变量对象构成的链表就是作用域链。

相关文章及原文 1、作用域/作用域链 2、作用域-笔试题 3、参考文章

08、闭包

多种不同的定义

1、闭包是指有权访问另一个函数作用域中的变量的函数。 2、闭包是一个拥有许多变量和绑定了这些变量的环境的表达式(通常是一个函数),因而这些变量也是该表达式的一部分。 3、闭包可以让一个函数访问并操作其声明时的作用域中的变量和函数,即使声明时的作用域消失了,也可以调用。 4、闭包是一个定义在其它函数(父函数)里面的函数,它拥有对父函数里面变量的访问权。闭包有三个作用域的访问权。自身的作用域、父作用域和全局作用域。 5、闭包就是能够读取其他函数内部变量的函数。

原文链接 1、闭包的定义

09、原型和原型链

概念

在JavaScript中,对象都有__proto__属性(隐式原型),指向构造该对象的构造函数的原型,而函数Function比较特殊,它除了和其他对象一样有__proto__属性外,还有自己特有的属性prototype称之为原型对象,原型对象有一个constructor属性,该属性指回该函数本身。

关键字介绍

1、每个函数都会有prototype属性,普通对象没有prototype属性。prototype是构造函数的原型对象。 2、每个对象都有双下划线__proto__属性,因为函数也是对象,所以函数也有双下划线__proto__属性。它指向构造函数的原型对象。 3、constructor是原型对象上的一个指向构造函数的属性。

原文链接 1、原型与原型链-总结

10、new操作符

new操作符做的事情

1、创建一个全新的对象,也就是使用Object.create(null)创建了无原型的对象。目的是保存new出来的实例的所有属性。 2、将构造函数的原型赋值给新创建的对象的原型。目的是将构造函数原型上的属性继承下来。 3、调用构造函数,并将this指向新建的对象。目的是让构造函数内的属性全部转交到该对象上,使得this指向改变,方法有三:apply、call、bind。 4、判断构造函数调用的方式,如果是new的调用方式,则返回经过加工后的新对象,如果是普通调用方式,则直接返回构造函数调用时的返回值。

通过new的方式创建对象和通过字面量创建对象有什么区别

对象都是通过new产生。function Foo() {},function是语法糖,内部等同于new Function()。 let object = { number: 1 } ,使用字面量创建对象,内部也是使用了new Object()。 对于创建一个对象来说,更推荐使用字面量的方式创建对象。因为使用new Object()的方式创建对象,需要通过作用域链一层层找到Object,如果使用字面量的方式就没有这个问题。

原文链接 1、原文

11、两等(==)和非(!)的优先级比较

代码示例

console.log([] == ![]); // true 

解析

1、!(非) 的优先级高于两等(==),右边[ ]是true,取返等于false 。 2、一个引用类型值和一个基本类型值作比较,会把引用类型转化成基本类型值。所以,[ ] => 0。 3、最后0 == false的结果是true。

原文链接 1、原文

12、数据类型转换

三种类型转换

1、转换为布尔值,调用Boolean()方法。 2、转换为数字,调用Number()、parseInt()和parseFloat()方法。 3、转换为字符串,调用.toString()或者String()方法。

原文链接 1、原文

13、检测数据类型的方法

最常见的判断方法:typeof

注意:其中typeof返回的类型都是字符串形式。

console.log(typeof "hello world"); // => "string"  console.log(typeof 'undefined'); // => "string" console.log(typeof 123); // => "number" console.log(typeof true); // => "boolean" console.log(typeof undefined); // => "undefined" console.log(typeof Symbol()); // => "symbol" console.log(typeof null); // => "object" console.log(typeof [1,2,3]); // => "object" console.log(typeof new Date()); // => "object" console.log(typeof new RegExp()); // => "object" console.log(typeof new Function()); // => "function" 

已知是对象类型:instanceof

注意:instanceof后面一定要是对象类型,并且大小写不能错,该方法适合一些条件选择或分支。

console.log([1,2,3] instanceof Array); // => true console.log(new Date() instanceof Date); // => true console.log(new Function() instanceof Function); // => true console.log(null instanceof Object); // => false 

根据对象原型链检测:Object.prototype.toString.call()

1、适用于所有类型的判断检测,注意区分大小写.toString()方法,在Object原型上返回数据格式。 2、原生检测方法,不存在兼容性问题。 3、call()调用且改变this指向,因为大部分数据类型都有自己的toString()方法。 4、toString()可以输出一个对象的内部属性class,查看对象的类型名。

console.log(Object.prototype.toString.call("123")); // => [object String] console.log(Object.prototype.toString.call(123)); // => [object Number] console.log(Object.prototype.toString.call(true)); // => [object Boolean] console.log(Object.prototype.toString.call(null)); // => [object Null] console.log(Object.prototype.toString.call(undefined)); // => [object Undefined] console.log(Object.prototype.toString.call(Symbol())); // => [object Symbol] console.log(Object.prototype.toString.call([1, 2, 3])); // => [object Array] console.log(Object.prototype.toString.call({name: 'Hello'})); // => [object Object] console.log(Object.prototype.toString.call(function () {})); // => [object Function] console.log(Object.prototype.toString.call(new Date())); // => [object Date] console.log(Object.prototype.toString.call(/\d/)); // => [object RegExp] 

根据对象的constructor进行检测

constructor判断方法跟instanceof相似,只是检测Object与instanceof不一样,constructor还可以处理基本数据类型的检测,不仅仅是对象类型。

注意

1、null和undefined没有constructor; 2、判断数字时使用圆括号,比如(123).constructor,如果写成123.constructor会报错; 3、constructor在类继承时会出错,因为Object会被覆盖掉,检测结果就不对。

// 注意当出现继承的时候,使用constructor会出现问题 function A() {}; function B() {}; A.prototype = new B(); // A继承自B console.log(A.constructor === B); // => false var C = new A(); // 现在开始判断C是否跟A的构造器一样 console.log(C.constructor === B); // => true console.log(C.constructor === A); // => false  // 解决这种情况,通常是手动调整对象的constructor指向 // 将自己的类赋值给对象的constructor属性 C.constructor = A; console.log(C.constructor === A); // => true console.log(C.constructor === B); // => false 

jQuery方法:jquery.type()/$.type()

据说是无敌万能的方法,如果对象是null或undefined,直接返回'null'和'undefined'。 注意:在使用时,一定要引入jQuery文件,不然会报错,jQuery is not defined**

console.log(jQuery.type(undefined) === "undefined"); // => true console.log(jQuery.type() === "undefined"); // => true console.log(jQuery.type(window.notDefined) === "undefined"); // => true console.log(jQuery.type(123) === "number"); // => true console.log(jQuery.type('123') === "string"); // => true console.log(jQuery.type([]) === "array"); // => true console.log(jQuery.type(true) === "boolean"); // => true console.log(jQuery.type(function(){}) === "function"); // => rue console.log(jQuery.type(new Date()) === "date"); // => true console.log(jQuery.type(/\d/) === "regexp"); // => true console.log(jQuery.type(new Error()) === "error"); // => true jQuery 版本高于 1.9.3 console.log(jQuery.type({name:'Hello'}) === "object"); // => true console.log(jQuery.type(Symbol()) === "symbol"); // => true 

原文链接 1、原文

14、检测一个对象是不是数组

源数据

// 基本数据类型 let number = 100; let string = 'asdfghjkl'; let boolean = true; let nu = null; let un = undefined; // 引用数据类型 let fun = function() {}; let object = {}; let array = [3, 6, 9]; let date = new Date(); 

proto

console.log(object.__proto__ == Array.prototype); // false console.log(array.__proto__ == Array.prototype); // true console.log(date.__proto__ == Array.prototype); // false 

Object.getPrototypeOf([value])

// __proto__可能被浏览器禁用,所以有等效的函数检测。 console.log(Object.getPrototypeOf(object) == Array.prototype); // false console.log(Object.getPrototypeOf(array) == Array.prototype); // true console.log(Object.getPrototypeOf(date) == Array.prototype); // false 

Array.prototype.isPrototypeOf([value])

console.log(Array.prototype.isPrototypeOf(object)); // false console.log(Array.prototype.isPrototypeOf(array)); // true console.log(Array.prototype.isPrototypeOf(date)); // false 

使用构造函数constructor进行检测,即使用父级原型对象中的constructor属性

console.log(object.constructor == Array); // false console.log(array.constructor == Array); // true console.log(date.constructor == Array); // false 

[value] instanceof Array

console.log(object instanceof Array); // false console.log(array instanceof Array); // true console.log(date instanceof Array); // false 

万能方法,不存在兼容性:[value].prototype.toString().call()

console.log(Object.prototype.toString.call(object)); // [object Object] console.log(Object.prototype.toString.call(array)); // [object Array] console.log(Object.prototype.toString.call(date)); // [object Date] 

isArray():ES5新增,存在兼容性问题。

console.log(Array.isArray(object)); // => false console.log(Array.isArray(array)); // => true console.log(Array.isArray(date)); // => false 

原文链接 1、原文

15、this

改变this指向之call/apply/bind

相同点 都可以改变this指向。 不同点 1、call和apply会调用函数,并且改变函数内部this指向。 2、call和apply传递的参数不一样,call传递参数使用逗号隔开,apply使用数组传递参数。 3、bind不会调用函数,可以改变函数内部this指向。 应用场景 1、call经常做继承。 2、apply经常跟数组有关系。比如,借助于数学对象实现数组最大值最小值。 3、bind不调用函数。但是可以改变this指向。比如改变定时器内部的this指向。

this总结

1、函数是否是用new来调用?如果是则this绑定新创建的对。 2、函数是否通过call或者apply来硬绑定调用?如果是则this绑定的是指定的对象。 3、函数是否是被某个上下文对象调用?如果是则this绑定这个上下文对象。 4、如果以上都不是,则使用默认绑定,点前是谁就是谁,函数调用一般指向window比较多。

this带图

1. 在浏览器里,在全局范围内this指向window对象。 2. 在函数中,this永远指向最后调用他的那个对象。 3. 构造函数中,this指向new出来的那个新的对象(实例)。 4.call、apply、bind中的this被强行绑定在指定的那个对象上。 5. 箭头函数中的this比较特殊,箭头函数的this指向父作用域中的this,不是调用时的this。要知道前四种方式,都是调用时确定,也就是动态的this。而箭头函数是静态的this,声明的时候就确定了。 6.call、apply、bind都是函数内置的一些API,调用时可以为函数指定this的执行,同时也可以传参。

原文链接 1、6种函数调用方式的this指向 2、改变this指向之call/apply/bind 3、this总结-06 4、this带图

16、ES6及往后版本的新特性 序号 关键字 功能 1 序号 let 2 const 定义常量 3 箭头函数 () => a + b; 4 解构赋值 let { a, b, c} = obj; 5 模板字符串 `` 6 扩展运算符 … 7 函数参数默认值 function a(a = 0, b = 0) { return a + b; } 8 函数参数不定参 function add(...num){ return num.reduce((result, value) => result + value) } add(1, 2, 3); // 6 9 class类的定义 class Point { constructor(x, y) { this.x = x; this.y = y; } } 17、箭头函数与普通函数有什么区别 18、字符串常用方法 序号 方法 说明 result 1 slice 下标截取字符串 此方法传入的参数是字符串下标,第一个参数要比第二个参数小,否则会返回不确定的值,截取的值包含第一个参数,不包含第二个参数。 2 substr 下标和长度截取字符串 此方法传入下标和长度,第一个参数传入下标,截取的值包含下包,第二个参数是截取长度 3 substring 下标截取字符串 此方法传入的参数是字符串下标,两个参数不区分大小,因为此方法会自动找到最小下标开始截取到结束下标,截取的值不包含结束下标 4 split 字符截取字符串 根据传入的参数对字符串进行分割,并且返回数组 5 replace 替换字符串内容 用另一个值替换在字符串中指定的值 19、数组常用方法 序号 API 功能描述 是否改变原数组 01 unshift 此方法是将一个或多个元素添加到数组的开头,并返回新数组的长度 Y 02 push 此方法是在数组的后面添加新加元素,此方法改变了数组的长度 Y 03 shift 此方法删除数组第一个元素,并返回数组,此方法改变了数组的长度 Y 04 pop 此方法删除数组最后一个元素,并返回数组,此方法改变了数组的长度 Y 05 join 此方法将数组转为字符串并返回转化的字符串数据,不会改变原来的数组 N 06 splice Array.splice(开始位置, 删除的个数,元素) ,万能方法,可以实现增删改 Y 07 slice 此方法截取指定位置的数组,并且返回截取的数组,不会改变原数组 N 08 map 此方法是将数组中的每个元素调用一个提供的函数,结果作为一个新的数组返回,并没有改变原来的数组 N 09 forEach 此方法是将数组中的每个元素执行传进提供的函数,没有返回值,注意和map方法区分 N 10 filter 此方法是将所有元素进行判断,将满足条件的元素作为一个新的数组返回 N 20、indexOf和findIndex的区别

概述

indexOf:方法返回在数组中可以找到一个给定元素的第一个索引(下标),如果不存在,返回-1。 findIndex:方法返回数组中满足提供的测试函数的第一个元素的索引(下标),如果不存在,返回-1。

indexOf代码示例

let dataA1 = ['王维', '范仲淹', '欧阳修', '李商隐', '孟浩然'], dataA2 = [{ sname: '陶渊明' }, { sname: '贺知章' }], variate = { sname: '岑参' }; console.log(dataA1.indexOf('欧阳修')); // 2 console.log(dataA2.indexOf({ sname: '陶渊明' })); // -1 console.log([{ sname: '龚自珍' }, variate].indexOf(variate)); // 1 

findIndex代码示例

let dataArray = [ { id: 1, sname: '陆游', alias: '务观', nickname: '放翁' }, { id: 2, sname: '屈原', alias: '原', nickname: null }, { id: 3, sname: '王安石', alias: '介甫', nickname: '半山' } ]; console.log(dataArray[dataArray.findIndex((item) => item.id == 3)]); // {id: 3, sname: "王安石", alias: "介甫", nickname: "半山"} // 注意:普通函数写法记得加 return console.log(dataArray[dataArray.findIndex(function(item) { return item.id == 2; })]); // {id: 2, sname: "屈原", alias: "原", nickname: null} 

indexOf与findIndex的区别

indexOf:查找值作为第一个参数,采用绝对相等进行比较,更多的是用于查找基本类型的数据,如果是引用类型,则是判断是否是同一个对象的引用。 findIndex:比较函数作为第一个参数,多用于非基本类型的数组索引查找,或查找条件很复杂。

indexOf与findIndex的相同点

indexOf与findIndex都是查找数组中满足条件的第一个元素的索引(下标)。

相关链接 1、源码实现 2、原文-1

21、双等与三等有什么区别 22、事件委托、代理

1、多种不同的定义

1、通俗的讲,事件委托就是把一个元素响应事件(click、keydown等)的函数委托到外层元素上。 2、简单的讲,事件委托就是利用JavaScript事件冒泡的特性,将内层元素的事件委托(绑定)给外层元素处理。 3、一般来讲,事件委托会把一个或者一组元素的事件委托(绑定)到它的父层元素或者更外层元素上,真正绑定事件的是外层元素,当事件响应到需要绑定的元素上时,会通过事件冒泡机制从而触发它的外层元素的绑定事件上,然后在外层元素上去执行函数。

2、原文链接 1、普通事件和事件委托

23、JavaScript之继承

1、原型直接继承 1、原型直接继承原型直接继承 2、知乎-原文

2、原型实例继承 1、原型实例继承 2、知乎-原文

3、构造函数继承 1、构造函数继承 2、知乎-原文

4、ES6的class继承 1、ES6的class继承 2、知乎-原文

5、原型实例继承和原型直接继承的区别 1、原型直接继承

student.prototype = parentClass.prototype; student.prototype.constructor = student; 

2、原型实例继承

student.prototype = new parentClass(); student.prototype.construct = student; 

3、相关链接 1、原型实例继承和原型直接继承的区别

18、图片懒加载

css部分

.img_box { margin-top: 30px; display: flex; flex-direction: column; justify-content: center; align-items: center; border: 1px solid red; } .img_box>img { width: 450px; height: 500px; margin: 5px 0; } 

html部分

<div class="img_box">    <img src="/img/loading02.gif" data-src="/img/01.jpg" alt=""> <img src="/img/loading02.gif" data-src="/img/02.jpg" alt=""> <img src="/img/loading02.gif" data-src="/img/03.jpg" alt=""> <img src="/img/loading02.gif" data-src="/img/04.jpg" alt=""> <img src="/img/loading02.gif" data-src="/img/05.jpg" alt=""> <img src="/img/loading02.gif" data-src="/img/06.jpg" alt=""> <img src="/img/loading02.gif" data-src="/img/07.jpg" alt=""> <img src="/img/loading02.gif" data-src="/img/08.jpg" alt=""> <img src="/img/loading02.gif" data-src="/img/09.jpg" alt="">  // 1、获取一面图片标签元素 // 获取图片列表, // 即img标签列表 let imgs = document.querySelectorAll('img'); // 2、获取到浏览器顶部的距离 function getTop(e) { return e.offsetTop; }; // 3、懒加载实现 function lazyload(dataImg) { // 3.1、获取可视区域高度 let innerHeight = window.innerHeight; // 3.2、获取滚动区域高度 let scrollTop = document.documentElement.scrollTop || document.body.scrollTop; for (let i = 0; i < dataImg.length; i++) { // 3.3、如果图片距离顶部的距离大于 // 可视区域和滚动区域之和时触发懒加载 if ((innerHeight + scrollTop) > getTop(dataImg[i])) { // 3.4、真实情况是页面开始有1秒空白, // 所以使用setTimeout定时1s (function(i) { setTimeout(function() { // 3.5、不加立即执行函数i会等于9 // 隐形加载图片或其他资源, // 创建一个临时图片, // 这个图片在内存中不会到页面上去。 // 实现隐形加载 let temp = new Image(); // console.log('new:', temp); // 3.6、只会请求一次 temp.src = dataImg[i].getAttribute('data-src'); // console.log('src:', temp); // 3.7、onload判断图片加载完毕, // 真实图片加载完毕, // 再赋值给DOM节点 temp.onload = function() { // 3.8、获取自定义属性data-src, // 用真图片替换假图片 dataImg[i].src = dataImg[i].getAttribute('data-src'); // console.log('dataImg:', dataImg[i].src); }; }, 1000); })(i); } } }; // 4、调用懒加载函数, // 传递图片标签元素 lazyload(imgs); // 5、滚动监听 window.onscroll = function() { // 调用懒加载函数 lazyload(imgs); }; }; 

相关链接 1、原文

20、JavaScript实现内置方法

原文链接 01、原文-mypush 02、原文-mymap 03、原文-myforEach 04、原文-myjoin 05、原文-myfindIndex 06、原文-myevery 07、原文-mysort 08、原文-myreduce 09、原文-myapply 10、原文-mybind 11、原文-mycall

JavaScript内置方法总结 1、掘金 2、掘金

21、函数柯理化

21.1、参数复用

// 普通函数验证 function check(regExp, text) { return regExp.test(text); }; console.log(check(/^\d+$/g, '123')); // true console.log(check(/^\d+$/g, '2d')); // false console.log(check(/^[a-z]+$/g, 'text')); // true console.log(check(/^[a-z]+$/g, '3d')); // false // -------------------------------------------------------------------------- // Currying后 function curryingCheck(regExp) { return function(regExp) { return reg.test(regExp); }; }; let hasNumber = curryingCheck(/^\d+$/g), hasLetter = curryingCheck(/^[a-z]+$/g); console.log(hasNumber('159')); // true console.log(hasNumber('2d')); // false console.log(hasLetter('3d')); // false console.log(hasLetter('text')); // true 

示例是一个正则的校验,正常来说直接调用check函数就可以,但是如果有很多地方都要校验是否有数字,其实就是需要将第一个参数reg进行复用,这样别的地方就能够直接调用hasNumber,hasLetter等函数,让参数能够复用,调用起来也更方便。

21.2、提前确认(惰性函数)

// 方案一 let on = function(element, event, handler) { if (document.addEventListener) { if (element && event && handler) { element.addEventListener(event, handler, false); }; } else { if (element && event && handler) { element.attachEvent('on' + event, handler); }; }; }; // 方案二 // ()(); => ~function() {.. ..}(); let on = (function() { if (document.addEventListener) { return function(element, event, handler) { if (element && event && handler) { element.addEventListener(event, handler, false); }; }; } else { return function(element, event, handler) { if (element && event && handler) { element.attachEvent('on' + event, handler); }; }; }; })(); // 方案三 // 换一种写法可能比较好理解一点, // 上面就是把isSupport这个参数给先确定下来了 let on = function(isSupport, element, event, handler) { isSupport = isSupport || document.addEventListener; if (isSupport) { return element.addEventListener(event, handler, false); } else { return element.attachEvent('on' + event, handler); }; }; 

在做项目的过程中,封装一些dom操作可以说再常见不过,上面第一种写法也是比较常见,但是看看第二种写法,它相对于第一种写法就是自执行然后返回一个新的函数,这样其实就是提前确定了会走哪一个方法,避免每次都进行判断。

21.3、延迟运行

Function.prototype.bind = function (context) { let that = this, args = Array.prototype.slice.call(arguments, 1); return function() { return that.apply(context, args); }; }; 

js中经常使用的bind,实现的机制就是Currying。

21.4、经典面试题

实现一个add()方法,使计算结果能够满足如下预期: add(1)(2)(3) = 6; add(1, 2, 3)(4) = 10; add(1)(2)(3)(4)(5) = 15;

function add() { // 第一次执行时,定义一个数组专门用来存储所有的参数 let _args = Array.prototype.slice.call(arguments); // 在内部声明一个函数, // 利用闭包的特性保存_args并收集所有的参数值 let _adder = function() { _args.push(...arguments); return _adder; }; // 利用toString隐式转换的特性,当最后执行时隐式转换, // 并计算最终的值返回 // toString是toScript()方法中的一个属性 _adder.toString = function () { return _args.reduce(function (a, b) { return a + b; }); }; return _adder; }; console.log(add(1)(2)(3)); // 6 console.log(add(1, 2, 3)(4)); // 10 console.log(add(1)(2)(3)(4)(5)); // 15 console.log(add(2, 6)(1)); // 9 

21.5、原文链接 1、函数柯理化

22、任务(事件)队列和事件循环

22.1、定义 22.1.1、任务(事件)队列

JavaScript中有两类任务(事件)队列:宏任务队列(macro tasks)和微任务队列(micro tasks)。宏任务队列可以有多个,微任务队列只有一个。

宏仁务(macro tasks)

1. setTimeout 2. setInterval 3. script 4. I/O 5. UI 交互事件 6. postMessage 7. MessageChannel 8. setImmediate(Nodejs环境)

微任务(micro tasks)

1. Promise.then 2. Object.observe 3. MutaionObserve 4. process.nextTick(Node.js环境)

22.1.2、事件循环

事件循环(Event Loop)遵循的是HTML5的标准。当执行栈(stack)为空的时候,就会从任务队列中,取任务来执行。共3步: 1、取一个宏任务来执行。执行完毕后,下一步。 2、取一个微任务来执行,执行完毕后,再取一个微任务来执行。直到微任务队列为空,执行下一步。 3、更新UI渲染。 事件循环(Event Loop)会无限循环执行上面的3步,这就是事件循环(Event Loop)的主要控制逻辑。其中,第3步(更新UI渲染)会根据浏览器的逻辑,决定要不要马上执行更新。毕竟更新UI成本大,所以,一般都会比较长的时间间隔,执行一次更新。

22.2、示例 22.2.1、示例-1

setTimeout(() => { console.log(1); }, 20); console.log(2); setTimeout(() => { console.log(3); }, 10); console.log(4); // console.time('time'); for (let i = 0; i < 90000000; i++) { // 900000 5 个 0:2.57421875 ms 左右 // 9000000 6 个 0:12.625 ms 左右 // 90000000 7 个 0:116.13525390625 ms 左右 // 900000000 8 个 0:525.622314453125 ms 左右 // 9000000000 9 个 0:9807.490966796875 ms 左右 // 90000000000 10 个 0:循环不出来了 } // console.timeEnd('time'); console.log(5); setTimeout(() => { console.log(6); }, 8); console.log(7); setTimeout(() => { console.log(8); }, 15); console.log(9); // 2 4 5 7 9   3 1 6 8 

循环的时间都超过了所有定时器的时间,在宏仁务中,定时器会按照从上到下的顺序执行,不再按照设置的时间长短来执行。

22.2.2、示例-2

console.log(1); setTimeout(()=>{ console.log(2); }, 50); console.log(3); setTimeout(()=>{ console.log(4); while(1 === 1) {}; // 遇到死循环, // 所有代码执行都是在主栈中执行, // 主栈永远结束不了, // 后面啥都不干 }, 0); console.log(5); // 1 3 5   4 

22.2.3、示例-3

console.log(1); // 宏仁务 setTimeout(function () { console.log(2); }, 0); // 微任务 Promise.resolve().then(function () { console.log(3); }).then(function () { console.log(4); }); console.log(5); // 1 5   3 4 2 

22.3、原文链接 1、定义 2、示例

23、正则表达式

1. 正则(RegExp)

24、实现浏览器内多个标签页(.html 页面)之间的通信

1、cookies 2、localStorge 3、webSocket 4、SharedWorker

25、面向对象(OOP)

封装

// 使用字面量定义不需要反复创建的对象 var object = { Uname: '张三', Uage: 56 }; // 如果一个对象需要反复创建,使用构造函数即可 // 第一步:定义构造函数 function Student(name, age) { this.name = name; this.age = age; // 不能将方法(函数)定义在构造函数中, // 因为会被多次创建,每创建一个实例就会创建一次方法。 }; // 第二步:用new调用构造函数 let newFun = new Student('半晨', '28'); console.log(newFun); // Student { name: '半晨', age: '28' } console.log(newFun.name); // 半晨 console.log(newFun.age); // 28 

继承

所有子对象共有的方法,应该添加到构造函数的原型对象中,子对象调用方法时,先在子对象本地查找。如果本地对象没有找到,才延原型链向父级对象查找,直到找到为止。

多态

如果从父对象继承来的方法不好用,可在对象本地定义同名方法,覆盖父对象中的方法(重写)。 强调:原型对象中,方法(函数)里面的this指向由调用该方法的点(.)前的某个子对象(实例)来决定。

原文链接 1、面向对象的三大特征

17、JavaScript的内置对象

1、掘金-原文

26、严格模式

1、掘金-严格模式

27、变量的提升

27.1、定义

变量的提升是JavaScript的默认行为,它会将所有变量声明移动到当前作用域的顶部,并且可以在声明之前使用变量。初始化不会被提升(赋值留在原地,函数除外),提升仅作用于变量的声明。

27.2、原文链接 1、JavaScript之变量提升-笔试题

30、事件捕获和事件冒泡

30.1、定义 30.1.1、事件捕获

事件捕获是从document开始发生,发生顺序一直向内,直到最后一个元素结束。

30.1.2、事件冒泡

事件冒泡是从最内层元素开始发生,发生顺序一直向外,直到document冒泡才结束。

30.2、扩展

1、对于非点击的节点,先执行捕获再执行冒泡。 2、对于被点击的节点,按照顺序执行先注册的事件。

30.3、原文链接 1、事件捕获和事件冒泡

31、设计模式

31.1、单例模式 31.1.1、概念

保证一个类仅有一个实例,并提供一个访问它的全局访问点。实现的方法为先判断实例存在与否,如果存在则直接返回,如果不存在就创建了再返回,这就确保了一个类只有一个实例对象。

31.1.2、适用场景

一个单一对象。比如:弹窗,无论点击多少次,弹窗只应该被创建一次。

31.1.3、代码实现

class CreateUser { constructor(name) { this.name = name; this.getName(); }; getName() { console.log(this.name); }; }; // 代理实现单例模式 let ProxyMode = (function () { let instance = null; return function (name) { if (!instance) instance = new CreateUser(name); return instance; }; })(); // 测试单体模式的实例 let a = new ProxyMode("aaa"); console.log(a); // CreateUser {name: "aaa"} let b = new ProxyMode("bbb"); console.log(b); // CreateUser {name: "aaa"} // 因为单体模式是只实例化一次, // 所以下面的实例是相等的 console.log(a === b); // true 

31.2、策略模式 31.2.1、概念

定义一系列的算法,把他们一个个封装起来,并且使他们可以相互替换。

31.2.2、目的

策略模式的目的就是将算法的使用和算法的实现分离开。

31.2.3、解释

一个基于策略模式的程序至少由两部分组成。第一个部分是一组策略类(可变),策略类封装了具体的算法,并负责具体的计算过程。第二个部分是环境类Context(不变),Context接受客户的请求,随后将请求委托给某一个策略类。要做到这一点,说明Context中要维持对某个策略对象的引用。

31.2.4、代码实现

// 策略类 let levelOBJ = { funA: function(money) { return money * 5; }, funB: function(money) { return money * 3; }, funC: function(money) { return money * 2; } }; let calculateBouns = function(level, money) { return levelOBJ[level](money); }; console.log(calculateBouns('funA', 10)); // 50 console.log(calculateBouns('funB', 20)); // 60 console.log(calculateBouns('funC', 30)); // 60 

31.3、代理模式 31.3.1、概念

为一个对象提供一个代用品或占位符,以便控制对它的访问。

31.3.2、常用的虚拟代理形式

某一个花销很大的操作,可以通过虚拟代理的方式延迟到这种需要它的时候才去创建(例:使用虚拟代理实现图片懒加载)。

31.3.3、图片懒加载的方式

先通过一张loading图占位,然后通过异步的方式加载图片,等图片加载好了再把完成的图片加载到img标签里面。

31.3.4、解释

使用代理模式实现图片懒加载的优点还有符合单一职责原则。减少一个类或方法的粒度和耦合度。

31.3.5、代码实现

let imgFunc = (function() { // 创建一个img标签 let imgNode = document.createElement('img'); // 把标签放到body上 document.body.appendChild(imgNode); // 返回setSrc函数 return { setSrc: function(src) { imgNode.src = src; } }; })(); let proxyImage = (function() { // 创建一个img标签 let img = new Image(); // 给img标签添加自执行函数 img.onload = function() { imgFunc.setSrc(this.src); }; return { setSrc: function(src) { imgFunc.setSrc('/img/loading02.gif'); setTimeout(() => { img.src = src; }, 1000); } }; })(); console.log(proxyImage); // {setSrc: ƒ} proxyImage.setSrc('/img/08.jpg'); 

31.4、中介者模式 31.4.1、概念

通过一个中介者对象,其他所有的相关对象都通过该中介者对象来通信,而不是相互引用,当其中的一个对象发生改变时,只需要通知中介者对象即可。通过中介者模式可以解除对象与对象之间的紧耦合关系。

31.4.2、例如

现实生活中,航线上的飞机只需要和机场的塔台通信就能确定航线和飞行状态,而不需要和所有飞机通信。同时塔台作为中介者,知道每架飞机的飞行状态,所以可以安排所有飞机的起降和航线安排。

31.4.3、适用的场景

例如购物车需求,存在商品选择表单、颜色选择表单、购买数量表单等,都会触发change事件,那么可以通过中介者来转发处理这些事件,实现各个事件间的解耦,仅仅维护中介者对象即可。

31.4.4、html

<div> <div> <span>选择颜色:  "red|32G": 3, "red|16G": 0, "blue|32G": 1, "blue|16G": 6 }; // 中介者对象 let mediator = (function () { let colorSelect = document.getElementById('colorSelect'), memorySelect = document.getElementById('memorySelect'), numberInput = document.getElementById('numberInput'), colorInfo = document.getElementById('colorInfo'), memoryInfo = document.getElementById('memoryInfo'), numberInfo = document.getElementById('numberInfo'), nextBtn = document.getElementById('nextBtn'), regNumber = /^[1-9]{1}[0-9]{0,2}$/; return { changed: function (obj) { // 颜色 let color = colorSelect.value, // 内存 memory = memorySelect.value, // 数量 number = numberInput.value, // 颜色和内存对应的手机库存数量 stock = goods[`${color}|${memory}`]; // 如果改变的是选择颜色下拉框 if (obj === colorSelect) { colorInfo.innerHTML = color; } else if (obj === memorySelect) { memoryInfo.innerHTML = memory; } else if (obj === numberInput) { numberInfo.innerHTML = number; } if (!color) return (nextBtn.disabled = true, nextBtn.innerHTML = '请选择手机颜色'); if (!memory) return (nextBtn.disabled = true, nextBtn.innerHTML = '请选择内存大小'); if (!regNumber.test(number)) return (nextBtn.disabled = true, nextBtn.innerHTML = '请输入正确的购买数量'); if (number > stock) return (nextBtn.disabled = true, nextBtn.innerHTML = '库存不足'); nextBtn.disabled = false; nextBtn.innerHTML = '放入购物车'; } } })(); // 事件函数 // id选择器可以直接绑定事件,不需要特意获取。 colorSelect.onchange = function () { mediator.changed(this); }; memorySelect.onchange = function () { mediator.changed(this); }; numberInput.oninput = function () { mediator.changed(this); }; 

31.5、装饰者模式 31.5.1、概念

在不改变对象自身的基础上,在程序运行期间给对象动态地添加方法。

31.5.2、例如

现有4种型号的自行车分别被定义成一个单独的类,如果给每辆自行车都加上前灯、尾灯、铃铛这3个配件,如果用类继承的方式,需要创建4 * 3 = 12个子类。但如果通过装饰者模式,只需要创建3个类。

31.5.3、适用的场景

原有方法维持不变,在原有方法上再挂载其他方法来满足现有需求;函数的解耦,将函数拆分成多个可复用的函数,再将拆分出来的函数挂载到某个函数上,实现相同的效果但增强了复用性。

31.5.4、用AOP装饰函数实现装饰者模式

Function.prototype.before = function(beforefn) { // 保存原函数引用 let self = this; // 返回包含了原函数和新函数的'代理函数' return function() { // 执行新函数,修正this beforefn.apply(this, arguments); // 执行原函数 return self.apply(this, arguments); }; }; Function.prototype.after = function(afterfn) { let self = this; return function() { let ret = self.apply(this, arguments); afterfn.apply(this, arguments); return ret; }; }; let func = function() { console.log('2'); }; // func1和func3为挂载函数 let func1 = function() { console.log('1'); }; let func3 = function() { console.log('3'); }; func = func.before(func1).after(func3); func(); // 1  2  3 

31.6、原文链接 1、单例模式 2、策略模式 3、代理模式 4、中介者模式 5、装饰者模式 6、掘金 - 原文

32、promise、async和await

1、promise/async/await 2、掘金-题目

33、垃圾回收机制 34、DOM 35、BOM 36、ajax 37、观察者模式

1、观察者模式 1、CSDN博客-原文 3、掘金-原文

38、发布订阅模式

1、发布订阅模式 2、CSDN 博客 - 原文 3、掘金-原文

41、原型链的prototype和__proto__的区别 ------------------------------zd------------------------------ 1、回调函数
  1. 回调函数
2、变量提升
  1. 变量提升
3、函数作用域 (scopes) 和函数作用域链
  1. scopes
4、闭包
  1. 闭包
5、统计字符串
  1. 统计字符串
6、this
  1. this - 示例
7、深浅克隆
  1. 深浅克隆
8、构造函数(constructor)
9、包装类型
  1. String - 包装类型
10、内置类
11、重写
12、面向对象
  1. 面向对象的三大特征
13、访问器属性
  1. 访问器属性
关注
打赏
1653961664
查看更多评论
立即登录/注册

微信扫码登录

0.3875s