jss

30 阅读1小时+

js运行在客户端上,是弱语言,脚本语言,跨平台语言,

自由变量的查找和this指向,闭包,垃圾回收,内存泄漏

1:object有哪些对象

2: 两种数据存储方式:(栈和堆)

3:Object.prototype.hasOwnProperty()

4:你对原型和原型链的了解有多少?

5:Node和Element

6:css提高页面性能

7: 如何理解作用域和作用域链

8: click在上有300ms延迟,原因及如何解决?

9: 数组去重

10:Object.is和Object.assgin

11:浏览器的事件传输机制原理你了解么

12:浏览器的主要组成部分是什么

13: 能来讲讲JS的语言特性吗

14:new Object()

15:FloatInt

16:连续赋值

17:promise

18:Event loop

19:将对象会字符串转为真正的数组

20:JS的继承

21:提高DOM性能

22:a[b], a.b23:字符串的不可变

24:mouseover和mouseenter的区别

25: 为什么建议把css放在head中,Script放在最后?

26:Seo

27:我们都知道作用域链,但是我们如何根据作用域链来

优化代码性能呢?

28:WeakMap

29:可枚举

30:浅拷贝和深拷贝

31:如何判断字符串是数字型的

32:for, for in, for of, forEach

33:数组和字符串内置方法

34:new的步骤

35:微前端

36:原型存储在内存哪里

37:如何判断给定的是一个数组

38:Reduce

39:暂时性死区

40:修饰符

41:arguments

42:js代码执行

43:写一个函数,第一秒打印1,第二秒打印2

44:判断数据类型

45:三种动态创建元素的区别

46:pc网络特效47:requestAnimationFrame

48:'string', String(), new String()

49:toString, Object.prototype.string()和

Object.prototype.string.call()的区别

50:es6新语法

51:map和object区别

52:Object.key

53:浏览器的事件机制有哪些?

54: sort

55:按位异或,与(&),或(|), 非,左移(<<),右

移(>>)

56:数组和链表

57:工程化,模块化,组件化

58:ajax与promise, axios

59:箭头函数跟普通函数的区别

60:defer和async

61:domcontentloaded

62:序列化实现深拷贝的问题

63:事件

64:let和const

65:reduce

66:symbol

67:Array.map()

68:正则表达式

69:JS判断是否是数组的四种做法70:防抖和节流

71:relative问题

72:纯函数

73:concat

74:Event 监听事件里 target 和 currentTarget 什么区

别?

75:Array(2) 和 Array.of(2)

76: js的四种内置对象

77:Object.create()、new Object()和{}的区别

78:判断object为空对象

79:用es5实现let

80:可迭代,可遍历

81: 通过delete删除数组,对象中的元素

82:冒泡,捕获

83:js执行上下文

84:回调函数

85:bind,call, apply

86:2-8的随机数

87: map和forEach

88:Css和js会阻碍dom渲染吗?

89:事件委托

90:JS定时器不可靠的原因及解决方案

91:纯函数

92: for in , for of

93:commonJs , Es694:css模块化方案

95:js为什么是单线程

96:模块化

97:为什么出现白屏,怎么解决

98:字符串等基本数据类型为什么有属性和方法

99:原型链

100:删除数组元素

自由变量

所有自由变量的查找,是在函数定义的地方,向上级作用域查找

this则相反,是在执行的地方

// 函数作为返回值

function fn() {

const a = 100

return function() {

// 函数定义的地方,没有a。则向上级作用域查找,找到100

console.log(a);

}

}

const a = 200

let q = fn()

q() // 100

// 函数作为参数

function fnn(fn) {

const a = 200

fn()

}

const a = 100

function fn() {

console.log(a);

}

fnn(fn) //100箭头函数里面打印的this和外面打印的this是一样的

const a = {

s: 20,

er() {

console.log(this); // a对象

setTimeout(() => {

console.log(this); // a对象

})

}

}

a.er()

const a = {

b: 10,

c: () => {

console.log(this)

}

}

a.c() // window

const a = {

b: 10,

c() {

console.log(this)

}

}

a.c() // a对象

const a = {

b: 10,父级作用域也就是上级作用域

上面的settimeout是本身出发的执行, 不是张三,所以为window

setTimeout( )是属于 window 的 method。

对象里定义一个定时器,不需要像调用函数那样加()

c: function() {

console.log(this)

}

}

a.c() // a对象

let obj = {

say() {

console.log(this);

},

qaa() {

() => {

console.log(this);

}

},

ww() {

setTimeout(() => {

console.log(this);

})

},

a: () =>{

console.log(this);

}

}

console.log(obj.say()); // obj

console.log(obj.qaa()); // und

console.log(obj.ww()); // obj

console.log(obj.a()); // win闭包

闭包是一种保护私有变量的机制,在函数执行时形成私有的作用域,保护里面的私有变量不受外界干

扰,使得私有变量不被JavaScript垃圾回收机制强制回收,

表现为函数作为返回值,函数作为参数

var obj = {

a: {

b: {

c: () => {

console.log(this);

}

}

},

c: 'true'

}

obj.a.b.c() // window, 对象没有作用域

var obj = {

a: {

b: function() {

setTimeout(() => {

console.log(this);

})

},

qq: 22

}

}

obj.a.b() // a对象

var x = 11

var obb = {

x: 222,

y: {

obc: () => {

console.log(this);

var x = 111

var obj = {

x: 22,

say: () => {

console.log(this.x);

}

}

obj.say()

}

}

}

obb.y.obc() // window, 11闭包的优点:一个函数可以访问另外一个函数的变量,可以保护私有变量不被全局变量污染。

闭包的缺点:过多使用闭包会造成内存占用过多的问题,

闭包本身会造成内部变量常驻内存

内部函数引用外部的函数的变量,外部函数执行完毕,作用域也不会删除。从而形成了一种不删除的独

立作用域。

闭包的应用

2.回调

3.函数防抖

4.封装私有变量

闭包是内存使用,不是内存泄漏。

但闭包不是内存泄漏,因为它是符合开发者预期的,即本身就这么设计的。而内存泄漏是非预期的。

// 闭包隐藏数据,只提供api

function fn() {

const date = {} // date被隐藏,不能被外界访问

return {

set(key, value) {

date[key] = value

},

get(key) {

return date[key]

}

}

}

let q = fn()

q.set('a', 50)

console.log(q.get('a'));

function f1(){

var n=999;

function f2(){

alert(n);在上面的代码中,函数f2就被包括在函数f1内部,这时f1内部的所有局部变量,对f2都是可见的。但是

反过来就不行,f2内部的局部变量,对f1就是不可见的。这就是Javascript语言特有的"链式作用域"结构

(chain scope),子对象会一级一级地向上寻找所有父对象的变量。所以,父对象的所有变量,对子对

象都是可见的,反之则不成立。

既然f2可以读取f1中的局部变量,那么只要把f2作为返回值,我们不就可以在f1外部读取f1中的变量了

吗!

n,就是闭包。

闭包可以用在许多地方。它的最大用处有两个,一个是前面提到的可以读取函数内部的变量,另一个就

是让这些变量的值始终保持在内存中。

由于闭包会使得函数中的变量都被保存在内存中,内存消耗很大,所以不能滥用闭包,否则会造成网页

的性能问题,解决方法是,在退出函数之前,将不使用的局部变量全部删除。

内存回收机制

JS的垃圾回收机制是为了以防内存泄漏,内存泄漏的含义就是当已经不需要某块内

存时这块内存还存在着,没有被释放,导致该内存无法被使用,垃圾回收机制就是

间歇的不定期的寻找到不再使用的变量,并释放掉它们所指向的内存。并且因为js

的对象等没有固定的大小,所以当他们大小已知时,才能对他们进行动态的存储分

配,最终都会释放这些内存,以便后续接着用。

但闭包不是内存泄漏,因为它是符合开发者预期的,即本身就这么设计的。而内存泄漏是非预期的。

WeakMap WeakSet 弱引用,不会影响垃圾回收。

内存的垃圾回收机制,不是实时的,而且是 JS 代码控制不了的,因此这里不一定能直接看到效果。

垃圾回收机制不考虑 WeakSet 对该对象的引用,也就是说,如果其他对象都不再引用该对象,那么垃圾

回收机制会自动回收该对象所占用的内存,不考虑该对象还存在于 WeakSet 之中。

JavaScript执行环境中的垃圾回收器怎样才能检测到哪块内存可以被回收呢?

通常有两种方式:标记清除(mark and sweep)、引用计数(reference counting)

}

return f2;

}

var result=f1();

result(); // 999标记清除

标记清除是现在最常使用的垃圾回收策略, 使用标记清除作为垃圾回收机制的浏览器会在垃圾回收程序进

行时会做如下几步 :

标记内存中所有的变量

把在上下文(全局作用域, 脚本作用域)中声明的变量,以及在全局被引用的变量的标记删除掉, 剩下的所有

带标记的变量就被视为要删除的变量, 垃圾回收执行时释放它们占用的内存

内存清理, 清除垃圾

伪代码简单说明一下 :

引用计数

引用计数是一种不常用的垃圾回收策略, 主要核心思路就是记录值被引用的次数, 一个值被赋给变量,引用

次数+1, 这个变量在某个时刻重新赋了一个新值, 旧值的引用次数-1变为了0, 在下次垃圾回收程序进行时

就会释放它的内存

引用计数存在的问题 : 循环引用

伪代码简单实例

伪代码简单说明一下 :

//变量 color,dog 在全局环境下声明, 不会被清除

const color = 'red';

var dog = '金毛';

{

let cat = 'kitty'; // 变量 cat 在块作用域中声明, 且没有被全局所引用, 所以会在下一次垃圾回收

执行时, 释放其内存

}在ie9以前,垃圾回收算法采用的是引用计数的方法。针对闭包,会导致内存泄漏。所以需要在不使用

时将其设置为null, 释放内存空间.

而在ie9以后,采用标记清除的方法。闭包是浏览器预期的,不会导致内存泄漏。

常见内存泄漏的原因:

(1)全局变量引起的内存泄露

(3)dom清空或删除时,事件未清除导致的内存泄漏

(4)循环引用带来的内存泄露

1

浏览器对象模型、文档对象模型和全局JavaScript对象三类

一、浏览器对象模型,最顶端是window对象,所有浏览器都支持 window 对象。

所有 JavaScript 全局对象、函数以及变量均自动成为 window 对象的成员。

window.open() - 打开新窗口 window.close() - 关闭当前窗口 window.moveTo() - 移动当前窗口

window.resizeTo() - 调整当前窗口的尺寸

1、document

document.getElementById //通过id获取元素

document.getElementsByTagName //通过标签名获取元素

document.referrer //获取上一个跳转页面的地址(需要服务器环境)

2、location

window.location.href //获取或者重定url地址

window.location.search //获取地址参数部分

function fn() {

const obj1 = new Object() // new Object 在堆内存中创建了一个对象1 {} 这个值被赋值给

obj1 于是引用次数 + 1

const obj2 = new Object() // new Object 在堆内存中创建了一个对象2 {} 这个值被赋值给

obj2 于是引用次数 + 1

obj1.a = obj2; // obj2 被赋值给 obj1的a属性 于是对象1的引用次数 1+1 = 2

obj2.a = obj1; // obj1 被赋值给 obj2的a属性 于是对象2的引用次数 1+1 = 2

n }

// 此时两个对象之间相互引用 且如果函数多次调用, 又会重新执行多次函数体, 又会多了n个相互引用的

对象占用内存

为了避免循环引用的问题 : 我们可以手动将其设置为nullwindow.location.hash //获取页面锚点或者叫哈希值

3、history

4、screen

screen对象用于获取用户的屏幕信息。

二、文档对象模型

DOM下,HTML文档各个节点被视为各种类型的Node对象。每个Node对象都有自己的属性和方法,利

用这些属性和方法可以遍历整个文档树。

 然后可以对这些node节点对象进行各种操作,如增删改查等等。

一、节点创建型API

1.1 createElement

2.3 removeChild(删除子元素)

三 节点查询型API(查)

3.1 document.getElementById

3.2 document.getElementsByTagName

3.3 document.getElementsByName

3.4 document.getElementsByClassName

3.5 document.querySelector和document.querySelectorAll

i

三、全局JavaScript对象

全局JavaScript对象的名字通常是首字母大写。

1、String:处理字符串

2、Number: 处理数字

3、Boolean;处理布尔值4、Date: 处理日期

5、Math:计算和处理数字

Object对象、RegExp对象、 Global对象、Function对象。

2

栈由操作系统自动分配释放 ,

堆由开发人员分配和释放, 若开发人员不释放,程序结束时由 OS 回收,

基本数据类型是直接存储在栈中的简单数据段,占据空间小、大小固定,属于被频繁使用的数据。栈是

存储基本类型值和执行代码的空间。

引用数据类型是存储在堆内存中,占据空间大、大小不固定。引用数据类型在栈中存储了指针,该指针

指向堆中该实体的起始地址,当解释器寻找引用值时,会检索其在栈中的地址,取得地址后从堆中获得

实体。

两种数据类型的区别:

  1. 堆比栈空间大,栈比堆运行速度快。

  2. 栈是连续的空间,而堆是不连续的空间,根据引用直接获取。

  3. 基础数据类型比较稳定,而且相对来说占用的内存小。

  4. 引用数据类型大小是动态的,而且是无限的。

3

hasOwnProperty() 方法会返回一个布尔值,指示对象自身属性中是否具有指定的属性。该方法会忽

略掉那些从原型链上继承到的属性。

const object1 = {};

object1.property1 = 42;

console.log(object1.hasOwnProperty('property1'));

// expected output: true

console.log(object1.hasOwnProperty('toString'));

// expected output: false

o = new Object();

o.propOne = null;

o.hasOwnProperty('propOne'); // 返回 true

o.propTwo = undefined;

o.hasOwnProperty('propTwo'); // 返回 true4

什么是原型

在javascript中,函数可以有属性。每个函数都有一个特殊的属性叫作原型(prototype)

什么是原型链

方法时,如果在当前对象中找不到定义,会继续在当前对象的原型对象中查找,如果原型对象中

依然没有找到,会继续在原型对象的原型中查找(原型也是对象,也有它自己的原型)如此继续,直

到找到为止,或者查找到最顶层的原型对象中也没有找到,就结束查找,返回undefined。可以看

出,这个查找过程是一个链式的查找,每个对象都有一个到它自身原型对象的链接,这些链接组件

的整个链条就是原型链

原型和原型链存在的意义是什么?

使得实例对象可以共享构造函数原型属性和方法,节省内存。构造函数原型上的属性和方法越多,

节省内存越大.

5

Dom是一棵树,所有节点都是Node

Node是Element的基类

Element是其他html元素的基类

HtmlCollection是Element的集合

NodeList是Node的集合

.childNodes会包含Text和Comment的节点

.children不会包含

6

1,减少生产包体积:

属性简写

删除0和单位

2,减少http请求节约带宽

图标替换

使用精灵图7

对象不能产生作用域

什么是作用域?

作用域是在运行时代码中的某些特定部分中变量,函数和对象的可访问性,作用域决定了代码区块

中变量和其他资源的可见性。

作用域存在的意义是什么?

作用域存在的最大意义就是变量隔离即:不同作用域下同名变量不会有冲突。

什么是作用域链?

当我们在某个函数的内部作用域中查找某个变量时,如果没有找到就会到他的父级作用域中查找,如

果父级也没找到就会接着一层一层的向上寻找,直到找到全局作用域还是没找到的话,就宣布放

弃。这种一层一层的作用域嵌套关系,就是作用域链

8

(1)粗暴型,禁用缩放

(2)利用FastClick,其原理是:

检测到touchend事件后,立刻出发模拟click事件,并且把浏览器300毫秒之后真正出发的事件给阻断掉

9

都只针对数组里面的元素全是基本的。引用的不行

let a = [[1,2], [1,2]]

console.log([...new Set(a)]); // [[1,2], [1,2]]

// 1

const a = [1,3,3,2,5,2,6]

console.log(Array.from(new Set(a))); // [1, 3, 2, 5, 6]

// Array.from将set结构转为数组

// 2

const a = [1,3,3,2,5,2,6]

console.log([...new Set(a)]); // [1, 3, 2, 5, 6]10

// 3 splice

let len = arr.length

for(let i = 0; i < len; i++) {

for (let j = i + 1; j < len; j++) {

if (arr[i] === arr[j]) {

arr.splice(j, 1)

len--

j--

}

}

}

// 4 indexOf

创建一个newArr,再遍历原数组arr。

判断newArr.indexOf(arr[i])=== -1, 为真则加到新数组。

// 4 includes

newArr.includes(arr[i])

// Object.is

存在就是为了‘===’的局限性

'==='局限性

-0 === +0 // true

NaN === NaN // false

Object.is(-0, +0) // false

Object.is(NaN, NaN) // true\

// Object.assign 浅拷贝

Object.assign方法用于对象的合并,将源对象(source)的所有可枚举属性,复制到目标对象

(target)。

Object.assign方法的第一个参数是目标对象,后面的参数都是源对象。

const target = { a: 1, b: 1 };

const source1 = { b: 2, c: 2 };11

事件传输机制冒泡和捕获分别由微软和网景公司提出

这两个概念都是为了解决页面中事件流(事件发生顺序)的问题

12

用户界面:包括地址栏、前进/后退按钮、书签菜单等。除了浏览器主窗口显示的您请求的页面外,

其他显示的各个部分都属于用户界面。

浏览器引擎:在用户界面和呈现引擎之间传送指令。

渲染引擎–负责显示请求的内容。如果请求的内容是HTML,它就负责解析HTML和CSS内容,并将

解析后的内容显示在屏幕上

13

运行在客户端浏览器上;

不用预编译,直接解析执行代码;

是弱类型语言,较为灵活;

与操作系统无关,跨平台的语言;

脚本语言、解释性语言

14

const source2 = { c: 3 };

Object.assign(target, source1, source2);

target // {a:1, b:2, c:3}151617JS是单线程运行的

异步要基于回调来实现

event loop就是异步回调的实现原理then和catch都会返回一个promise对象

(4)Promise方法:all()

all()方法可以完成并行任务, 它接收一个数组,数组的每一项都是一个 promise对象。当数组中所

有的 promise的状态都达到 resolved的时候,all方法的状态就会变成 resolved,如果有一个状态变成

了 rejected,那么 all方法的状态就会变成 rejected。

new Promise((resolve, reject) => {

console.log('初始化');

resolve(); // 必须有,主要是指定promise的状态,没有的话,只能打印第一个

})

.then(() => {

throw new Error('有哪里不对了');

console.log('执行「这个」”');

})

.catch(() => {

console.log('执行「那个」');

})

.then(() => {

console.log('执行「这个」,无论前面发生了什么');

});

let promise1 = new Promise((resolve,reject)=>{

setTimeout(()=>{

resolve(1);

},2000)

});

let promise2 = new Promise((resolve,reject)=>{(5)Promise方法:race()

race() 接受的参数也是一个每项都是 promise的数组,当最先执行完的事件执行完之后,就直接返

回该 promise对象的值。如果第一个 promise对象状态变成 resolved,那自身的状态变成了resolved;

反之第一个 promise变成 rejected,那自身状态就会变成 rejected。

finally接收一个回调函数,在promise结束时,无论结果是fulfilled或者是rejected,都会执行指定该回

调函数。这避免了同样的语句需要在then()和catch()中各写一次的情况。

async/await

异步回调callback hell

Promise then catch 链式调用,但也是基于回调函数

async/await是同步语法,彻底消灭回调函数

setTimeout(()=>{

resolve(2);

},1000)

});

Promise.all([promise1,promise2]).then(res=>{

console.log(res); //[1,2]

})

let promise1 = new Promise((resolve,reject)=>{

setTimeout(()=>{

reject(1);

},2000)

});

let promise2 = new Promise((resolve,reject)=>{

setTimeout(()=>{

resolve(2);

},1000)

});

Promise.race([promise1,promise2]).then(res=>{

console.log(res);

//结果:2

},rej=>{

console.log(rej)};

)promise的缺点是什么?

①promise一旦新建就会立即执行,无法中途取消

②如果不设置回调函数,promise内部抛出的错误,不会反应到外部

③当它处于pending状态时,无法得知目前进展到哪一个阶段(刚刚开始还是即将完成

promise如何捕获异常:

Promise捕获异常这里有两种方式

第一种单独对 .then() 中指定异常处理函数(第一种一般用在,希望捕获异常然后不影响接下里Promise

的执行)

我们只需要在 .then() 中添加两个function就好了,第二个是用来处理失败的情况。

第二种使用.catch来实现全部捕获(第二种一般用在,当一个Promise发生了异常,剩下的Promise都不

在执行)

在 Promise 中发生的未捕获异常不会被 window.onerror 捕获,如果你的页面使用了 前端异常监控平

台 这样的东西,会导致无法自动收集未捕获的 Promise 异常。比如你在 Promise 中,在不主动 catch 的情况下,如果使用 throw new Error() 或者 reject(‘error’),都

会变成 Uncaught (in promise) Error 而不会被 window.onerror 捕获。

解决

如果你想要在全局捕获这类的错误,有两种方法:

1、改写 Promise,使用一个非系统原生的 Promise 库,并改写内部异常捕获的逻辑,将异常抛出。(适

合框架开发者)

2、在 Promise 中异步抛出异常,比如 setTimeout(function(){ throw new Error() }) 来抛出想被全局捕

获的异常。(适合业务开发者)

18

异步不会阻塞代码执行

同步会阻塞代码执行过程

同步代码,一行一行放在Call Stack 执行

遇到异步,会先“记录”下。如果是微任务(promise, async/await),直接放到微任务队列里。如

果是宏任务(定时器,ajax, DOM事件),则放到web API里。时机到了,就移动到Callback

Queue。

如Call Stack为空(即同步代码执行完), 先尝试DOM渲染, Event Loop开始工作

先轮询查找微任务队列,有则移动到call stack中执行。

继续轮询查找(永动机一样), 每次轮询都会尝试DOM渲染 。

DOM渲染完,再轮询查找Callback Queue ,如果有则移动到call stack中执行。

异步( setTimeout , ajax等)使用回调,基于event loop

DOM 事件也使用回调,基于event loop

19

// Object.values

let a = {x: 34, y: 67}

console.log(Object.values(a)); // [34, 67]

// for in

let a = {x: 34, y: 67}

let arr = []

for (let key in a) {

arr.push(a[key])

}

console.log(arr); // [34, 67]20

ES5的继承实质上是先创建子类的实例对象this,然后再将父类的方法添加到this上

(Parent.apply(this)),然后继承原型。

ES6的继承机制完全不同,它是在子类的构造器中先调用super方法,创建出父类实例对象this,然后再

去修改子类中的this完善子类。

Js的继承是一个面向对象的概念。面向对象编程的三大特性,封装继承多态。我们常说java是面向对象

的,c语言是面向过程的。但实际上面向对象只是一个编程范式,他不是某种编程语言特有的。像c语

言,也是可以实现面向对象的。在c++里面,面向对象class关键字去声明一个类,在类里面写构造函

数。像js的面向对象有一个特点,他不是基于类来实现面向对象,而是通过原型来实现的。而es6之前是

没有class关键字,构造函数是直接声明的。es6之后有了class,

Class A exteds B实现继承,但本质还是基于原型,只是一种语法糖。

继承的三种方法

1,原型链

// Object.keys

let a = {x: 2, y: 9}

let arr = []

Object.keys(a).forEach(item => {

arr.push(a[item])

})

console.log(arr);

// Array.from,只能用在类数组对象上。

let a = {

0: 12,

1: 34,

2: 56,

'length': 3

}

console.log(Array.from(a)); // [12, 34, 56], 对象必须要有length属性

// 就是将一个类数组对象或者可遍历对象转换成一个真正的数组。

function Father(name) {

this.name = 'tang'

}

Father.prototype.age = 22

function Son(name) {

this.name = 'jia'

}2:构造函数

Son.prototype = new Father() // 重点

const son1 = new Son()

const son2 = new Son()

console.log(son1.name) //jia

console.log(son1.age) // 22

console.log(son2.age); // 22

son2.age = 33

console.log(son1.age) // 22

console.log(son2.age); // 33

优点

可以继承父类构造函数的属性,父类原型的属性

缺点

1,不能传参

2,父类构造函数的原型被所有实例对象共享

function Father(name) {

this.name = 'tang'

}

Father.prototype.age = 22

function Son(name) {

Father.call(this)

this.name = 'jia'

}

const son1 = new Son()

console.log(son1.name) // jia

console.log(son1.age) // und

优点

创建子类实例时,可以向父类传递参数

缺点

1、只能继承父类构造函数的属性,不能继承父构造函数的原型

2、无法实现构造函数的复用。(每次用每次都要重新调用)3,组合

4:class继承

21

DOM操作非常“昂贵”,避免频繁的DOM 操作

2,对DOM查询做缓存

3,将频繁DOM操作改为一次性操作

function Father(name) {

this.name = 'tang'

}

Father.prototype.age = 22

function Son(name) {

Father.call(this)

this.name = 'jia'

}

Son.prototype = new Father()

const son1 = new Son()

console.log(son1.name) // jia

console.log(son1.age) // 22

function Father(name) {

this.name = 'tang'

}

Father.prototype.age = 22

class Son extends Father {

constructor(name) {

super(name)

}

}

const son1 = new Son()

console.log(son1.name) // tang

console.log(son1.age) // 222,

3

22

var obj = {

x: 23

}

console.log(obj.x); // 23

var b = 'x'

console.log(obj.b); // unde23

字符串的不可变,看上去值改变了, 实际上是地址变了,内存中开辟了新的内存空间。

字符串的所有方法都不会影响原字符串,操作会返回一个新的

console.log(obj[b]); // 23

let a = {a: 30}

let b = {b: 10}

obj = {

a: 10

}

obj[a] = 50

obj[b] = 20

console.log(obj); // a: 10, [object Object]: 20

console.log(obj[a]); // 20 添加一个对象作为属性, 被自动转为字符串。

console.log(obj.a); // 10 取原有的属性

console.log([1,2,3].toString()); '1,2,3'

console.log({x:2}.toString()); // '[object Object]'

console.log(Object.prototype.toString.call({x:2})); // '[object Object]'

console.log(Object.prototype.toString.call([1,2,3])); // [object Array]

const a = { }

const b = { c:2 }

a[b]= 3

console.log(a) //{ '[object Object]': 3 }

const a = { }

const b = [1,2,3]

a[b]= 3

console.log(a) // {1,2,3: 3}

// 用的toString()

let str = 'ewdefef' // split, 用于把一个字符串分隔为字符串数组

console.log(str.charAt(2)); // d

console.log(str.charCodeAt(2)); // 100

console.log(str.substr(1, 3)); // wde

console.log(str.slice(2, 4)); // de

console.log(str.substring(2, 4)); // de24

mouseover:当鼠标移入元素或其子元素都会触发事件,所以有一个重复触发,冒泡的过程。对应的移

除事件是mouseout

mouseenter:当鼠标移除元素本身(不包含元素的子元素)会触发事件,也就是不会冒泡,对应的移

除事件是mouseleave

25

可以在dom树渲染前就构建cssom树,等到dom树完成就直接生成渲染树。

如果放在html后面,浏览器会先生成结构,再渲染样式。就好比一个正常字体,突然变大一样,容易感

觉卡顿

// A 65 a97 0 48

console.log(str.replace('w', 'q')); // eqdefef

console.log(str.split('')); //['e', 'w', 'd', 'e', 'f', 'e', 'f']

console.log(str.split(' ')); // ['ewdefef']

console.log(str.split()); // ['ewdefef']

console.log(str.toUpperCase()); // EWDEFEF

console.log(str.toLowerCase()); //

let a = [1,2,3,4]

let b = a.slice() // [1,2,3,4]

let c = a.slice(0, -1) // [1,2,3]

let d = a.splice() // []

substr跟数组splice一致

slice跟substring一样

//数组,

let arr = [1,2,3]

arr.join() // '1,2,3'

arr.join('') // '123'

arr.join(' ') // '1 2 3'

arr.join(',') // '1,2,3'

arr.join('|') // '1|2|3'Script为什么放在最后(script里有可以改变页面的样式)。

希望页面加载完以后再去执行script,可以使用户尽快看到页面。

如果不然,会先渲染一部分页面,然后卡顿去修改页面样式,再完成剩下的页面,这样页面的渲染时间

就变长了。

26

是搜索引擎优化。是一种方式:利用搜索引擎的规则提高网站在有关搜索引擎内的自然排名。目的是让

其在行业内占据领先地位,获得品牌收益。很大程度上是网站经营者的一种商业行为,将自己或自己公

司的排名前移。

搜索引擎优化,又称为SEO,即SearchEngineOptimization,它是一种通过分析搜索引擎的排名规律,了

解各种搜索引擎怎样进行搜索、怎样抓取互联网页面、怎样确定特定关键词的搜索结果排名的技术。搜

索引擎采用易于被搜索引用的手段,对网站进行有针对性的优化,提高网站在搜索引擎中的自然排名,

吸引更多的用户访问网站,提高网站的访问量,提高网站的销售能力和宣传能力,从而提升网站的品牌

效应。

27

作用域链是从当前作用域逐层查找的,一直查找到window,那么这样一个查找是要花费时间的,如果

我们让他马上查到这个变量。我们是不是就节省了这个时间。所以我们可以把我们要使用的变量,作为

参数传入方法,或者再方法里进行缓存,来提升速度。

28

WeakMap 专门做弱引用的,因此 WeakMap 只接受对象作为键名( null 除外),不接受其他类型的

值作为键名。其他的无意义 wMap.set(obj, 100)

代码执行完毕之后,obj 会被销毁,wMap 中也不再存在。但我们无法第一时间看到效果。因为:// 内

存的垃圾回收机制,不是实时的,而且是 JS 代码控制不了的,因此这里不一定能直接看到效果。

weakMap的key只能是引用类型

29

可遍历就是可枚举

for...in 循环:只遍历对象自身的和继承的可枚举的属性。

Object.keys() :返回对象自身的所有可枚举的属性的键名。

JSON.stringify() :只串行化对象自身的可枚举的属性。

Object.assign() : 忽略 enumerable 为 false 的属性,只拷贝对象自身的可枚举的属性。js中基本类型,基本包装类型的原型属性是不可枚举的(不可被 for…in… 遍历),

简单来说,用户定义的属性都是可枚举的,而内置对象不可枚举。

一种情况除外:当属性是继承于其它对象原型时,这时用户定义的属性就是不可枚举的

30简单点来说,就是假设B复制了A,当修改B时,看A是否会发生变化,如果A也跟着变了,说明这是浅拷

贝,拿人手短,如果B没变,那就是深拷贝,自食其力。

像是拷贝基本数据类型就是深拷贝。

浅拷贝

1.Object.assign()

Object.assign()方法是ES6中Object内置对象的方法,该方法可以用于js对象的合并等多个用途,其中一

个用途就是可以进行浅拷贝。

语法:Object . assgin ( target , …sources )

target:拷贝的目标对象\

sources:拷贝的来源对象,可以是多个来源\

使用Object.assgin()要注意几点:

它不会拷贝对象的继承属性

它不会拷贝对象的不可枚举属性

可以拷贝Symbol类型的属性

{...obj}等价于Object.assign({}, obj)

let obj = {x: 20}

let aa = {...obj}

aa.x = 90

console.log(obj);// {x: 20}

let obj = {x: 20}

let s = {}

Object.assign(s, obj)

s.x = 90

console.log(s); // {x: 90}

console.log(obj); // {x: 20}2,扩展运算符

3,concat

4,slice

Object.assign并不是只能实现浅拷贝,也可以实现深拷贝

浅拷贝

let obj = {x: 30}

let aa = Object.assign(obj)

obj.x = 60

console.log(obj); // 60

console.log(aa); // 60

let obj = {x: 30, aa: {bb: 30, cc: 50}} // 两层

let aa = Object.assign({}, obj)

obj.aa.bb = 60

obj.x = 50

console.log(obj); // 50, 60, 50

console.log(aa); // 30, 60, 50

深拷贝

let obj = {x: 30}

let aa = Object.assign({},obj)

obj.x = 60

console.log(obj); // 60

console.log(aa); // 30

对于Object.assign()而言,如果对象的属性值为简单类型(string,number),通过

Object.assign({},srcobj);得到的新对象为深拷贝;如果属性值为对象或其他引用类型,那对于这个对

象而言其实是浅拷贝的,这是Object.assign()特别需要注意的地方。

————————————————

版权声明:本文为CSDN博主「你好像很好吃a」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文

出处链接及本声明。

原文链接:blog.csdn.net/weixin_4429…

深拷贝的原理和实现

1.JSON.stringify

JSON.stringify()是目前开发过程中最简单的深拷贝方法,其实就是把一个对象序列化成为JSON的字符

串,并将对象里面的内容转换成字符串,最后再用JSON.parse()的方法将JSON字符串生成一个新的对象

let arr= [1, 2, { a: 1 }];

let cloneArr = JSON.parse(JSON.stringify(arr));

cloneArr[2].a = 3;

console.log(cloneArr);//[1, 2, { a: 3 }]

console.log(arr);//[1, 2, { a: 1 }]

2:递归,一层一层的拷贝

31

可以利用Number()函数和isNaN()函数来判断字符串是否是数字,语法“isNaN(Number("字符串",10)”;

如果返回true,则该字符串不是数字,否则是数字

32

这些结果可以看出,只有for…in可以对对象进行遍历(遍历的是对象的key),而for…of和forEach都不

能对对象进行遍历,for的编写方式更是没办法遍历

使用for…in是输出索引值,且索引值是string类型,不仅返回的是数组的下标,而且将数组的原型对象

以及数组对象本身属性值都会返回;通过索引值能拿到数组数据;

使用forEach可以输出索引值和数组值,而且不会输出数组的原型对象;

使用for of无法输出索引值,但也不会输出数组的原型对象。

使用for可以输出索引值和数组值,而且不会输出数组的原型对象;

let obj = {

a: undefined

}

console.log(JSON.stringify(obj)); // {}在实际工作开发中,数组的原型对象很可能是不需要的,全部列举出来可能会产生新的问题。

为了解决原型对象这个问题,可以使用 hasOwnProperty

for(let key in arr){ if(arr.hasOwnProperty(key)) { console.log(key); } } //输出的结果是 0 1 2 name

这个结果能看出来,虽然使用hasOwnProperty,但是数组本身的属性还是会输出

结果可以得出结论:forEach和for…of都可以迭代map/set,而for,for…in无法迭代map/set;

这里有个点要注意,set的forEach中第一个参数是value值,第二个值依然是value值;map的forEach中

第一个参数是value值,第二个值是对应的key值

forEach不能退出循环本身,return只能控制跳出单次循环,不支持break和continue,不可中断循环

for…in 适用于纯对象的遍历,能输出可枚举属性,不太适用数组,不能用于map/set对象

for…of 适用于无需知道索引值的数组遍历,另外对于字符串,arguments类数组,map/set对象的迭代

也更适用

forEach适用于需要知道索引值的数组遍历,可遍历map/set对象,但是最大的特点是不能中断

for循环适用于数据量大,或者兼容性要求高的情况

33

Array

改变原数组:splice, reserve, push,pop

不改变原数组: slice,indexOf,toString().concat() [...], filter, map

Array 属性

属性 描述

constructor 返回创建 Array 对象原型的函数。

length 设置或返回数组中元素的数量。

prototype 允许您向数组添加属性和方法。


Array 方法

concat() 连接两个或多个数组,并返回已连接数组的副本。

find() 返回数组中第一个通过测试的元素的值。

find() 返回数组中第一个通过测试的元素的值。

findIndex() 返回数组中通过测试的第一个元素的索引。

includes() 检查数组是否包含指定的元素。join()是数组特有的

string

34

indexOf() 在数组中搜索元素并返回其位置。

isArray() 检查对象是否为数组。

join() 将数组的所有元素连接成一个字符串。

map() 使用为每个数组元素调用函数的结果创建新数组。

pop() 删除数组的最后一个元素,并返回该元素。

push() 将新元素添加到数组的末尾,并返回新的长度。

reduce() 将数组的值减为单个值(从左到右)。

reverse() 反转数组中元素的顺序。

toString() 将数组转换为字符串,并返回结果。

String 对象属性

属性 描述

constructor 对创建该对象的函数的引用

length 字符串的长度

prototype 允许您向对象添加属性和方法


String 对象方法

split是字符串特有的

substr, substring, slice, indexOf, charAt, charCodeAt,

toUpperCase, toLowerCase, replace,

match() 找到一个或多个正则表达式的匹配。

toString() 返回字符串。

var str="Mr Blue has a blue house and a blue car";

var n=str.replace(/blue/g,"red"); // Mr Blue has a red house and a red car

var str="Mr Blue has a blue house and a blue car";

var n=str.replace(/blue/gi, "red");

// Mr red has a red house and a red car35

微前端是多个独立发布功能的团队一起构建现代化web应用程序的技术、策略和方法,将大而可怕的事

物分割成更小、更易于管理的部分,然后明确它们之间的依赖关系。应用开发的技术选择、代码库、团

队以及发布过程都能够相互独立地操作,而不需要过度的协调。

微前端架构是一种类似于微服务的架构,它将微服务的理念应用于浏览器端,即将 Web 应用由单一的

单体应用转变为多个能够独立开发、测试、部署的小型前端应用,而在用户看来仍然是内聚的单个产

品。

36

37

  • 创建一个空对象

  • 空对象的__proto__指向函数的prototype

  • 让函数的this指向空对象,执行函数的代码

  • 判断返回类型,是值类型,返回创建的对象。是引用类型,就返回这个引用类型38

39

在代码块内,使用let、const命令声明变量之前,该变量都是不可用的。这在语法上,称为“暂时性死区”

或者说,不能在一个变量还没声明时就去使用它。在直到声明它的代码之前的区域都属于暂时性死区

40

41

let aa = [1,23]

console.log(Object.prototype.toString.call(aa)); // [object Array]

console.log(aa instanceof Array); // true

log(Array.isArray)

function fn() {

let a = arguments.length

let b = arguments[2]

console.log(b); // 8

console.log(a); // 5

}

console.log(fn(1,25,8,9,5));伪数组, 不具备pop, push

arguments 是一个对应于传递给函数的参数的类数组对象

类数组”意味着 arguments 有 长度 属性 并且属性的索引是从零开始的,但是它没有 Array的 内置方

法,例如 forEach() 和 map()都是没有的。详情可以看 §Description.

arguments对象是所有(非箭头)函数中都可用的局部变量。你可以使用arguments对象在函数中引用

函数的参数。此对象包含传递给函数的每个参数,第一个参数在索引 0 处。例如,如果一个函数传递了

三个参数,你可以以如下方式引用他们:

42

JavaScript代码是由浏览器中的JavaScript解析器来执行的。JavaScript解析器在运行JavaScript代码的时

候分为两步:预解析和代码执行。

预解析:在当前作用域下,JS代码执行之前,浏览器会默认把带有var和function声明的变量在内存中

进行提前声明或者定义。

代码执行:从上到下执行JS语句。

预解析只会发生在通过var定义的变量和function 上。学习预解析能够让我们知道为什么在变量声

明之前访问变量的值是undefined,为什么在函数声明之前就可以调用函数。

43

for(let i=0;i<5;i++){

setTimeout(function(){

console.log(i)

},1000*i)

}

for(var i=0;i<5;i++){

(function(i){

setTimeout(function(){

console.log(i)

},1000*i)

})(i)

}44

1,typeof

后面跟要判断的数据,返回这个数据的详细类型,返回的结果用字符串表示。可以判断基本数据类型。

而引用数据类型只能判断对象和函数

2:instanceof

instanceof用来判断A是否为B的实例,表达式为:A instanceof B,如果A是B的实例,则返回true,否

则返回false。instanceof检测的是原型,内部机制是通过判断对象的原型链中是否有类型的原型。

3: constructor

当执行 var f = new F() 时,F 被当成了构造函数,f 是F的实例对象,此时 F 原型上的 constructor 传递

到了 f 上,因此 f.constructor == F

这样做的意义是,让新对象在诞生以后,就具有可追溯的数据类型。

总结:

  1. null 和 undefined 是无效的对象,因此是不会有 constructor 存在的,这两种类型的数据需要通过

其他方式来判断。

let a = Symbol(123)

console.log(typeof a); // 'symbol'

console.log(typeof null); //'object'

console.log(typeof [12,3]);

let b = function fn() {}

console.log(typeof b); //'function'

console.log(typeof undefined); //'undefinedd'

{} instanceof Object; //true

[] instanceof Array; //true

[] instanceof Object; //true

"123" instanceof String; //false

new String(123) instanceof String; //true

'a'.constructor == String

new Number(1).constructor == Number

true.constructor == Boolean

new Date().constructor == Date // 全是true4.Object.prototype.toString()

toString()是Object的原型方法,调用该方法,默认返回当前对象的[[Class]]。这是一个内部属性,其格

式为[object Xxx],其中Xxx就是对象的类型。

对于Object对象,直接调用toString()就能返回[object Object],而对于其他对象,则需要通过call、

apply来调用才能返回正确的类型信息。

45

46

offset: 偏移量

offsetWidth: 宽度 + padding + border, 不带单位

offsetHeight: 高度 + padding + border, 不带单位

offsetTop: 返回元素带有相对定位父元素上方的偏移offsetLeft: 返回元素带有相对定位父元素左边框的偏移

e.pageX: 鼠标在页面中横坐标

client

clientWidth: 宽度 + padding

cilentHeight:

cilentTop: border-top

cilentLeftt:

scrollscrollwidth: 宽度 + padding

scrollTop: 内容被卷去的高度+border-top

scrollHeight: scrollTop + 内容高度

47

大多数电脑显示器的刷新频率是60Hz, 就是每秒重绘60次。

优点

将多次dom操作集中在一块,在一次重绘中就完成

针对隐藏或不可见的,不进行重绘

是浏览器专门为动画提供的api

4849

为什么不能直接toString()

toString:由名字可以看出此方法是将传入的数据类型转换成字符串输出(null和undefined除外)

在JavaScript中,所有类都继承于Object,因此toString()方法应该也被继承了,但由上述可见事实并不

像我们想的那样,其实各数据类型使用toString()后的结果表现不一的原因在于:所有类在继承Object的

时候,改写了toString()方法。 Object原型上的方法是可以输出数据类型的。因此我们想判断数据类型

时,也只能使用原始方法。继而有了此方法:Object.prototype.toString.call(obj)

我们可以验证一下,将数组的toString方法删除,看看会是什么结果:

为啥要有call?

除了对象,其他属性都要用call。

因为读取的是Object的方法。方法指向Object。

let a = 'aaa'

let b = String('aaa')

let c = new String()

console.log(typeof a); // str

console.log(typeof b); // str

console.log(typeof c); // object

console.log(a === b); // true

console.log(a === c); // false

[1,2,3].toString() // '1,2,3'

let a = [1,2]

console.log(a.toString()); // '1,2'

console.log(Array.prototype.hasOwnProperty('toString')); //true

delete Array.prototype.toString;

console.log(Array.prototype.hasOwnProperty('toString')); //false

console.log(a.toString()); //"[object Array]"

let a = [1,2,3]

let b = {x: 20}

let c = function fn() {}

let d = 123

let e = false

console.log(Object.prototype.toString(c)); // [object, Object]

console.log(Object.prototype.toString(d)); // [object, Object]50

①let const关键字

②扩展运算符...

③模板字符串

④promise

⑤async/await

⑦class类(原型链语法糖)

⑧解构(对象or数组)

⑨箭头函数

⑩函数传参可设默认值

⑾新增 数据 类型 symbol set map weakset weakmap

⑿新增数组方法 find findIndex fill includes

51

①构造方式不同

//map

const map = new Map()

const map1 = new Map([['a',1],['b',2]])

//obj

const obj = new Object()

const obj1 = Object.create()

②object键的类型必须是String或者Symbol、map键的类型可以是任意类型

③object中key是无序的,map中可以是有序的,按照插入的顺序返回

④object只能通过Object.key()方法或for in统计数量,map有map.size

⑤object可以通过点或中括号访问属性,map用map.get()

⑥object不具备Iterator特性,不能for of遍历,map的keys()、values()、entries()都具有迭代器

⑦object可以用JSON.stringify()进行序列化,map只能转化成JSON,不能被parse解析

⑧应用场景:object做 数据 存储,需要序列化时使用;map频繁更新键值对,key类型未知时使用

52

Object.key

// 简单数组

const arr = ['a', 'b', 'c'];

console.log(Object.keys(arr)); // console: ['0', '1', '2']

// 类数组对象

const obj = { 0: 'a', 1: 'b', 2: 'c' };53

①DOM0级事件模型:这种模型不会传播,没有事件流的概念,可以在网页中直接定义监听函数,也可

以通过js属性来指定监听函数,直接在dom对象上注册事件名字。

②IE事件模型:一次事件有两个过程,事件处理阶段和事件冒泡阶段。事件处理会首先执行目标元素绑

定的监听事件,然后从目标元素冒泡到document,依次检查经过的节点是否绑定事件监听函数,如果

有则执行。通过attachEvent来添加监听函数,可以添加多个,会依次执行。

③DOM2级事件模型:事件有三个过程,事件捕获、事件触发和事件冒泡

54

console.log(Object.keys(obj)); // console: ['0', '1', '2']

console.log(Object.keys(obj).length); // 3

// 具有随机键顺序的类数组对象

const anObj = { 100: 'a', 2: 'b', 7: 'c' };

console.log(Object.keys(anObj)); // console: ['2', '7', '100']

// getFoo 是一个不可枚举的属性

const myObj = Object.create({}, {

getFoo: {

value() { return this.foo; }

}

});

myObj.foo = 1;

console.log(Object.keys(myObj)); // console: ['foo']

compareFn(a, b) 返回值 排序顺序

0 a 在 b 后

< 0 a 在 b 前

=== 0 保持 a 和 b 的顺序

所以,比较函数格式如下:

function compareFn(a, b) {

if (在某些排序规则中,a 小于 b) {

return -1;

}

if (在这一排序规则下,a 大于 b) {

return 1;

}

// a 一定等于 b

return 0;

}55

异或(相同取0,不同取一)

与(&) 都为1,才取一

或(|)

let a = 5; // 00000000000000000000000000000101

a ^= 3; // 00000000000000000000000000000011

console.log(a); // 00000000000000000000000000000110

// expected output: 6

let a = 5, b = 0

c = a ^ b // 5

let a = 5, b = 5

c = a ^ b // 0

a^b^a // b

0&0=0;0&1=0;1&0=0;1&1=1

即:两个同时为1,结果为1,否则为0

例如:3&5

十进制3转为二进制的3:0000 0011

十进制5转为二进制的5:0000 0101

------------------------结果:0000 0001 ->转为十进制:1

即:3&5 = 1左移(<< )

右移(>>)

56

概述

  

数组 是将元素在内存中连续存放,由于每个元素占用内存相同,可以通过下标迅速访问数组中任

何元素。但是如果要在数组中增加一个元素,需要移动大量元素,在内存中空出一个元素的空间,然后

将要增加的元素放在其中。删除也是。如果应用需要快速访问数据,很少插入和删除元素,就应该用数

组。

  

链表 中的元素在内存中不是顺序存储的,而是通过存在元素中的指针联系到一起,每个结点包括

两个部分:一个是存储 数据元素 的 数据域,另一个是存储下一个结点地址的 指针。

  如果要访问链表中一个元素,需要从第一个元素开始,一直找到需要的元素位置。但是增加和删除

一个元素对于链表数据结构就非常简单了,只要修改元素中的指针就可以了。如果应用需要经常插入和

删除元素你就需要用链表。

链表头节点可以插入删除,尾节点只能插入。如果删除,会导致前一个结点没有指向。

内存存储区别

数组从栈中分配空间, 对于程序员方便快速,但自由度小。

链表从堆中分配空间, 自由度大但申请管理比较麻烦.

0|0=0; 0|1=1; 1|0=1; 1|1=1;

即 :参加运算的两个对象,一个为1,其值为1。

例如:3|5 即 00000011 | 0000 0101 = 00000111,因此,3|5=7。

5<< 1 = 10

101整体向左移动一位,1010=10

5>>1 = 257

工程化

前端工程化是一种高层次思想而不是某种技术,所谓前端工程化就是将前端项目当成一项系统工程进行

分析、组织和构建从而达到项目结构清晰、分工明确、团队配合默契、开发效率提高的目的。而模块化

和组件化是为工程化思想下相对较具体的开发方式,因此可以简单的认为模块化和组件化是工程化的表

现形式。

模块化

模块化的优势:

  1. 提高维护性

  2. 提高代码复用率

  3. 方便依赖关系的管理

  4. 可以避免变量污染,命名冲突

三种模块化规范

  1. CommonJS

    

根据 CommonJS 规范,一个单独的文件就是一个模块。每一个模块都是一个单独的作用域,也就

是说,在该模块内部定义的变量,无法被其他模块读取,除非定义为global对象的属性。

  1. AMD

    AMD 即 Asynchronous Module Definition,中文名是异步模块定义的意思。AMD规范通过 define

方法去定义模块,通过require方法去加载模块。RequireJS 实现了这种规范。

  1. CMD

    CMD 即 Common Module Definition 通用模块定义,是 SeaJS 在推广过程中对模块定义的规范化

产出。

组件化

  1. 页面上的每个独立的、可视/可交互区域视为一个组件。

  2. 每个组件对应一个工程目录,组件所需的各种资源都在这个目录下就近维护

  3. 由于组件具有独立性,因此组件与组件之间可以 自由组合

  4. 页面只不过是组件的容器,负责组合组件形成功能完整的界面

  5. 当不需要某个组件,或者想要替换组件时,可以整个目录删除/替换

模块化主要体现的是一种分而治之的思想。分而治之是软件工程的重要思想,是复杂系统开发和维护的基石。

复制代码6. 组件化将页面视为一个容器,页面上各个独立部分例如:头部、导航、焦点图、侧边栏、底部等视

为独立组件,不同的页面根据内容的需要,去盛放相关组件即可组成完整的页面。

58

AJAX,是 Asynchronous JavaScript And XML,意思是利用JavaScript执行异步网络请求,对页面进行

局部更新,而不需要重载页面。

AJAX是浏览器的功能,利用浏览器在window上增加的XMLHttpRequest函数,利用该函数构造出一个对

象,使用该对象进行请求发送与响应接受。

  1. 创建一个 XMLHttpRequest 对象

  2. 调用对象的 open 方法启用。

  3. 监听对象的 onreadystatechange 事件(或者是 onload 和 onerror 事件),处理函数对返回的

数据进行处理。

  1. 调用对象的 send 方法发送请求。 完整版:

将原生的ajax封装成promise

const xhr = new XMLHttpRequest()

xhr.open('GET', '/data/test.json' ,true)

xhr.onreadystatechange =function(){

if(xhr.readyState === 4){

if(xhr.status === 200){

console.log(JSON.parse(xhr.responseText))

}

else{

console.log("其他");

}

}

}

xhr.send(null)

var myNewAjax=function(url){

return new Promise(function(resolve,reject){

var xhr = new XMLHttpRequest();axios

axios是通过Promise实现对ajax技术的一种封装,就像jquery对ajax的封装一样,简单来说就是ajax技

术实现了局部数据的刷新,axios实现了对ajax的封装,axios有的ajax都有,ajax有的axios不一定有,

在浏览器中创建 XMLHttpRequests

2、在node.js则创建http请求

3、支持Promise API

4、支持拦截请求和响应

5、转换请求和响应数据

6、取消请求

7、自动转换成JSON数据格式

8、客户端支持防御XSRF

9、提供了一些并发请求的接口(重要)

59

箭头函数表达式的语法比函数表达式更简洁,更适用于那些本来需要匿名函数的地方,并且它不能用作构

造函数。

箭头函数是有proto属性的,所以箭头函数本身是存在原型链的,他也是有自己的构造函数的,但是原

型链到箭头函数这一环就停止了,因为它自己没有prototype属性,没法让他的实例的proto属性指向,

所以箭头函数也就无法作为构造函数。

同时我们可以看到,由于箭头函数没有自己的this指针,通过 call() 或 apply() 方法调用一个函数时,只

能传递参数,不能绑定this,所以call()和apply()的第一个参数会被忽略。

xhr.open('get',url);

xhr.send(data);

xhr.onreadystatechange=function(){

if(xhr.status==200&&readyState==4){

var json=JSON.parse(xhr.responseText);

resolve(json)

}else if(xhr.readyState==4&&xhr.status!=200){

reject('error');

}

}

})

}如果箭头函数能作为构造函数,能够使用new运算符,那么整个过程中,无论是this对象,prototype属

性还是call()函数进行this绑定都无法处理,所以对箭头函数使用new运算符会抛出Error.

但是箭头函数都是匿名函数

箭头函数不能用于构造函数,不能使用new, 和 new一起用会抛出错误

箭头函数的 this 永远指向其上下文的 this ,任何方法都改变不了其指向,如 call() , bind() ,

apply().普通函数的this指向调用它的那个对象

箭头函数不绑定arguments

参考:(25条消息) 箭头函数为什么不能作为构造函数?_哈哩噜啾啾哈呀呀的博客-CSDN博客

60

本质区别:一个下载完,等dom渲染完才执行。一个下载完直接执行(async)

脚本下载和dom渲染是异步的,一块进行。

执行和dom渲染必须同步,一个一个来。

关于 defer,是按照加载顺序执行脚本的

标记为async的脚本并不保证按照指定它们的先后顺序执行。61

当初始的html文档被完全加载和解析完成之后,会促发domcontentloaded事件。无需等待样式表和图

片的加载。

html文档被完全加载和解析完成,以下事情都已完成当文档中没有脚本时,浏览器解析完html文档便能触发 DOMContentLoaded 事件;如果文档中包含js

脚本,则脚本会阻塞文档的解析,而脚本需要等 CSSOM 构建完成才能执行。

62

3.data内若属性的键值为undefined、函数或symbol,其会把该属性丢失。

4.data内若属性的键值有NaN、Infinity和-Infinity,其会变成null

5.data内若属性的键值由自定义构造函数生成的,转换后会丢弃对象的constructor。

6.data中若存在循环引用的情况也无法正确实现深拷贝。

63

当为某个容器绑定了 onmouseover 或者onmouseout 事件时,如果这个容器中有其它元素节点,那么

鼠标在内部移动时会频繁触发 onmouseover和onmouseout 事件。

function Person(name) {

this.name = name;

}

Person.prototype.speak = function(){

console.log('i can speak')

}

var arr1 = {

a:1,

b:undefined,

c:new Date('2019-12-24'),

d:new Person('mily'),

e:new RegExp('\w+'),

f:NaN,

g:Symbol("foo"),

h:{

total:12,

list:[{name:'小明',age:34},{name:'小红',age:23}]

}

}

// JSON.parse(JSON.stringify())方式

console.log(JSON.parse(JSON.stringify(arr1))

//结果为

//{

// a: 1

// c: "2019-12-24T00:00:00.000Z"

// d: {name: "mily"}

// e: {}

// f: null

// h: {total: 12, list: Array(2)}

// }为什么会出现这个原因呢?其实是因为事件冒泡导致的。当鼠标移上或者移出容器中的子节点时,

会分别触发mouseover和mouseout事件,紧随着dom树向上冒泡传递,直到被事件处理程序(监听

器)捕获捕获或者冒泡到根节点(document或者window),也就是说事件会向它的父级对象派发

Js事件监听onclick onmouseover

事件绑定 a.onclick,, a.addeventlistener

第三个参数false冒泡

True捕获

事件循环(event loop)

事件委托 lo ul

时间机制

事件

事件可能是用户在某些内容上的点击,鼠标经过某个特定元素或按下键盘上的某些按键,事件还可能是

web浏览器中发生的事情,比如说某个web页面加载完成,或者是用户滚动窗口或改变窗口大小。IE的

事件流是事件冒泡流(event bubbling),而Netscape的事件流是事件捕获流(event capturing)。

通俗来讲就是,就是当设定了多个div的嵌套时;即建立了父子关系,当父div与子div共同加入了onclick

事件时,当触发了子div的onclick事件后,子div进行相应的js操作,但是父div的onclick事件同样会被触

发。

阻止事件冒泡

e.stopPropagation();

js 监听浏览器窗口缩放

window , addEventListener (' res ize ',

64

let a = 20

a = 30 // 可以重复赋值

let a = 20

let a = 30 // 不能重复声明一:局部作用域(let,const)

Let有局部作用域,因此for循环的定时器就很适合let,每个i都有自己单独的子作用域。

For里面写个定时器,获取i的值。

二:不存在变量提升。(let,const)

三:暂时性死区(let,const)

因此会导致暂时性死区。只要块级作用域内存在let命令,那么他所声明的变量就绑定这个区域,不受外

部的影响

If (true) {

A= 12

Let A

报错

四:不能重复声明一个变量(let, const)

Const

Const声明一个只读的常量。

只声明不赋值会报错

Const并不是说变量的值不能改动,而是变量指向的那个地址不得改动。

对于引用。只要保证变量指向的那个地址固定,至于自身可不可变不重要。

const aa = [1, 2, 3]

aa[1] = 5

aa[3] = 4

aa.push(6)

console.log(aa); //[1,5,3,4,6]

aa = [5,6,7]

console.log(aa); // 报错

const a = []

a = [1,2,3]

log(a) // 报错65

reduce() 方法接收一个函数作为累加器 ,reduce 为数组中的每一个元素依次执行回调函数,不包括数组

中被删除或从未被赋值的元素,接受四个参数:初始值(上一次回调的返回值),当前元素值,当前索

引,原数组

语法: arr.reduce(callback,[initialValue])

去重

数组拍平

const a = {}

a.age = 20

console.log(a); // {age: 20}

callback:函数中包含四个参数``

  • previousValue (上一次调用回调返回的值,或者是提供的初始值(initialValue))

  • currentValue (数组中当前被处理的元素)

  • index (当前元素在数组中的索引)

  • array (调用的数组)

initialValue (作为第一次调用 callback 的第一个参数。)

let ee = ['a', 'b', 'a', 'b', 'c', 'e', 'e', 'c', 'd', 'd', 'd', 'd']

let aa = ee.reduce(function(q,w) {

if (q.indexOf(w) === -1) {

q.push(w)

}

return q

}, [])

console.log(aa);

深度

function tang(aa) {

return aa.reduce((a,b) => {

return a.concat(Array.isArray(b) ? tang(b) : b)

}, [])

}

let aa = [1,2,[3,[5,7,8]],5]66

每个从 Symbol() 返回的 symbol 值都是唯一的。一个 symbol 值能作为对象属性的标识符;这是该数

据类型仅有的目的。

所以现在对象属性名可以为两种类型:一种就是原本的字符串类型,一种即为新增的 symbol 类型。凡

是使用 symbol 命名的属性都是独一无二的,保证不与其他属性名产生冲突。

它不支持语法:" new Symbol() "。

Symbol.for()

如果我们要重复使用一个 symbol 时,可以用到 Symbol.for() 方法。Symbol.for() 方法接受一个字符串

参数,会在全局中搜索有没有以该参数命名的 symbol 的值,如果查找到就返回这个值。如果没有查到

则重新生成一个值,并将该值以参数名称注册到全局。

console.log(tang(aa));

一层

return aa.reduce((a,b) => {

return a.concat(b)

}, [])

}

let aa = {x: 3}

let bb = {y: 5}

const cc = {

q: 34

}

cc[aa] = 35

cc[bb] = 36

let dd = Symbol(33)

let ee = Symbol(33)

cc[dd] = 37

cc[ee] = 38

console.log(cc);

// {q: 34, [object Object]: 36, Symbol(33): 37, Symbol(33): 38}参数

描述

currentValue

必须。当前元素的值

index

可选。当前元素的索引值

arr

可选。当前元素属于的数组对象

let s1 = Symbol.for('sym'); // 创建

let s2 = Symbol.for('sym'); // 查找

s1 === s2; // true

Symbol.keyFor()

方法表示获取一个 symbol 的值在全局中注册的命名参数 key,只有使用 Symbol.for() 创建的值才会有

注册的命名参数,使用 Symbol() 生成的值则没有:

let s4 = Symbol('sym');

let s5 = Symbol.for('sym');

Symbol.keyFor(s4); // undefined

Symbol.keyFor(s5); // sym

67

thisValue作为第二个参数,传递给function函数,用作 "this" 的值。

如果省略了 thisValue,或者传入 null、undefined,那么回调函数的 this 为全局对象。

68

array.map(function(currentValue,index,arr), thisValue)

let a = [1,2,3,4]

let y = {a:2}

let q = a.map(function(a) {

console.log(this); // {a:2},是function的this

return a * 2

}, y)

console.log(q); // [2,4,6,8]正则表达式:regular expression

创建方式:

方法一: new

var str = new RegExp(//)

方法二:字面量

var str = //

test: 用来检测字符串是否满足正则表达式的要求

var ss = /ab/

ss.test('aa')// false

/ab/ 只要包含ab,就返回true

/[ab]/ 只要包含a | b 就返回true

/^ab$/ 只能是ab

/^[ab]$/ 只能是a | b

/^[a-z]$/ 26英文字母任意一个

/^[^a-z]$/ 中括号里面有^,代表取反

/^(ab){3}$/ 小括号优先级最高,只能是ababab

/^ab{3}$/ 只能是abbb

| 或

\d 0-9

\D 取反

\w 数字,字母,下划线

\W 取反

str.replace(/激情/, '**') 替换

g 全局

i 忽略大小写

{1,} // 1到无穷

{1, } // 报错,不能加空格

想要判断字符串里是否有$,要加转义符\,否则报错

/$/

// ${str}, str is any

var a = /${\w{1,}}/

console.log(a.test('${str}')); // true方法

应用

console.log(a.test('${flsdfei}')); // true

// match

let str = "I love JavaScript";

let result = str.match(/Java(Script)/g);

console.log( result[0] ); // JavaScript

console.log( result.length ); // 1

如果没有匹配项,则无论是否带有标记 g ,都将返回 null

let str = "I love JavaScript";

let result = str.match(/HTML/);

console.log(result); // null

// search

let str = "A drop of ink may make a million think";

console.log( str.search( /ink/i ) ); // 10(第一个匹配位置)

// replace

const reg1=/javascript/i;

const reg2=/javascript/ig;

console.log('hello Javascript Javascript Javascript'.replace(reg1,'js'));

//hello js Javascript Javascript

console.log('hello Javascript Javascript Javascript'.replace(reg2,'js'));

//hello js js js

// test

let str = "I love JavaScript";

// 这两个测试相同

console.log( /love/i.test(str) ); // true69

1.通过instanceof判断

2.通过constructor判断 我们知道,实例的构造函数属性constructor指向构造函数,那么通过

constructor属性也可以判断是否为一个数组。

3.通过Object.prototype.toString.call()判断

4.通过Array.isArray()判断

70

如:浏览器的 resize、scroll、keypress、mousemove 等事件在触发时,会不断地调用绑定在事件上

的回调函数,极大地浪费资源,降低前端性能

为了优化体验,需要对这类事件进行调用次数的限制,对此我们就可以采用throttle(防抖)和

debounce(节流)的方式来减少调用频率

验证QQ合法性(5~15位、全是数字、不以0开头):

const reg = /^[1-9][0-9]{4,14}$/

const isvalid = patrn.exec(s)

校验用户账号合法性(只能输入5-20个以字母开头、可带数字、“_”、“.”的字串):

var patrn=/^[a-zA-Z]{1}([a-zA-Z0-9]|[._]){4,19}$/;

const isvalid = patrn.exec(s)

let a = [1,3,4];

a.constructor === Array;//true防抖(debounce) : 在n秒后才会执行。如果n秒内被重复触发,

则重新计时

应用场景

(1) 用户在输入框中连续输入一串字符后,只会在输入完后去执行最后一次 的查询ajax请求,这样可以有

效减少请求次数,节约请求资源;

(2) window的resize、scroll事件,不断地调整浏览器的窗口大小、或者滚 动时会触发对应事件,防抖

让其只触发一次;

节流: (n秒内只执行一次,若在n秒内重复触发,只有一次生效)

应用场景

(1)鼠标连续不断地触发某事件(如点击),只在单位时间内只触发一次;

(2)页面上有一个可拖拽的盒子,想要获取坐标值。没人情况下会频繁触发。因此可以设置一个时间间

隔,每隔多少秒才去打印一次坐标

防抖:最后一次生效

节流:第一次生效(n秒内)

71

4 关于position定位,下列说法错误的是()

A fixed元素,可定位于相对于浏览器窗口的指定坐标,它始终是以 body 为依据

B relative元素以它原来的位置为基准偏移,在其移动后,原来的位置不再占据空间

C absolute 的元素,如果它的父容器设置了 position 属性,并且 position 的属性值为 absolute

或者 relative,那么就会依据父容器进行偏移

D fixed 属性的元素在标准流中不占位置

答案:B

relative会在标准流占据位置,但是是占据一开始的位置。top以后,会压住别的元素72

纯函数,当输入的值确定时,输出的值也是确定的。无状态的,更好做单元测试

.A{

width: 200px;

height: 200px;

background-color: red;

}

.B{

width: 100px;

height: 100px;

background-color: aqua;

}

.A{

position: relative; // 不脱离文档流,始终占据位置,下面不会顶上来

top: 50px; // 原来位置占着,去移动

}73

74

event.target: 触发当前事件的元素

event.currentTarget: 绑定当前事件的元素

let a = [1,2,3]

a.concat(4) // [1,2,3,4]

a.concat([4,5]) // [1,2,3,4,5]

a.concat([4,5,[6]]) // [1,2,3,4,5,[6]]

只能扁平一层

不会影响原数组。75

new Array 和 Array都是用来构建数组的,但是存在一些问题

这时,Array.of()

Array.of ()基本上可以用来替代 Array() 或 new Array() ,并且不存在由于参数不同而导致的重载。它的

行为非常统一。Array.of ()总是返回参数值组成的数组。如果没有参数,就返回一个空数组。

Array.of() // []

Array.of(undefined) // [undefined]

Array.of(1) // [1]

Array.of(1, 2) // [1, 2]

76

1.Array对象:提供一个数组的模型来存储大量有序的数据。

2.Math对象:可以处理所有的数学运算 。

3.Date对象:可以处理日期和时间的存储、转化和表达。

4.String对象:可以处理所有的字符串的操作。

我们本来是想创建元素5的

let a = Array(5)

log(a) // [空属性 × 5]

let a = Array.of(5)

log(a) // [5]77

字面量和new关键字创建的对象是Object的实例,原型指向Object.prototype,继承内置对象Object

Object.create(arg, pro)创建的对象的原型取决于arg,arg为null,新对象是空对象,没有原型,不继承

任何对象;arg为指定对象,新对象的原型指向指定对象,继承指定对象.

arg是必填参数,只能是(一个对象或者 null )。

如果为 null ,那新对象就彻彻底底是个空对象,没有继承 Object.prototype 上的任何属性和方法,

如 hasOwnProperty()、toString() 等。

————————————————

版权声明:本文为CSDN博主「yxiuzhu」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出

处链接及本声明。

原文链接:blog.csdn.net/haotian1997…

78

79

判断object为空对象

let obj = {}

// one

console.log(JSON.stringify(obj) === '{}');

// two

function fn(obj) {

for (let key in obj) {

return false

}

return true

}

console.log(fn(obj));

// three

console.log(Object.keys(obj).length === 0);80

可遍历:但凡是能够将每一项取出来进行一定操作的。像obj,array, string, map, set

可迭代:能够被for of遍历的都是可以迭代的,所以说普通的obj不可以进行迭代。但是类数组对象可以

迭代。

但凡是不能被Array.from或者扩展运算符([])操作的都是不可迭代。

81

82

// 用es5实现let

(function fn() {

var a = 5

console.log(a); //5

})()

console.log(a); // a is not defined

// 使用匿名函数和闭包来模拟let

// 用匿名函数的作用域来模拟let的块级作用域,不会造成污染。

// 类数组

let a = {

0: 3, // 必须从0开始,必须有length属性

1: 4,

'length': 1

}

let a = [1,2,3]

delete a[1]

log(a) // [1, empty , 3]

let o = {name: '1', age: 'a'}

delete o.name

log(o) // {age: 'a'}.a {

width: 30px;

height: 30px;

background-color: red;

}

.b {

width: 70px;

height: 70px;

background-color: blue;

}

.c {

width: 150px;

height: 150px;

background-color:yellow;

}