前端---JS篇


es6 的新特性

ES5和ES6有什么区别?(重点,有时15分有时6分)
1.通过不同的语法作答(说服面试官 你新技术是会的)

2.准备几个技术点去讲:优化 性能 缺陷

1、ES6新增了箭头函数,ES5没有;
2、ES6中新增了块级作用域,ES5没有;
3、ES6引入Class概念,不再像ES5一样使用原型链实现继承;
4、ES6中可以设置默认函数参数,ES5不行;
5、ES6中新增了promise特性。
6、ES6中有模板字符串$

setTimeout和setInterval的区别

setTimeout是一次性的暂停
setInterval是连续型的暂停

使用setTimeout实现一个setInteval

function timer(){
var time=setTimeout(function(){
document.write(2)
timer();
clearTimeout(time)
},1000)
}
timer()

setTimeout 和requestAnimationFrame

FPS(屏幕刷新率)
目前大多数设备的屏幕刷新率为 60 次/秒,每个帧的预算时间是16.66 毫秒 (1秒/60),1s 60帧,所以每一帧分到的时间是 1000/60 ≈ 16 ms。所以我们书写代码时力求不让一帧的工作量超过 16ms。

CommonJs和ES6的区别

首先两个都是js模块化的
不同点是:commonjs是对模块的值的拷贝,换句话说就是它如果数据改变不会去影响他原本的值;而es6是对数据值的引用,那么他就会改变值。
下面是commonjs的引用

// lib.js
var counter = 3;
function incCounter() {
  counter++;
}
module.exports = {
  counter: counter,
  incCounter: incCounter,
};
//main.js文件
var mod = require('./lib');
console.log(mod.counter);  // 3
mod.incCounter();
console.log(mod.counter); // 3

下面是ES6

// lib.js
export let counter = 3;
export function incCounter() {
  counter++;
}

// main.js
import { counter, incCounter } from './lib';
console.log(counter); // 3
incCounter();
console.log(counter); // 4

第二点就是commonjs是运行时加载,而es6是编译时加载。因为commonjs是(module.export)使用的对象,当代码脚本运行完才完成。即在输入时是先加载整个模块,生成一个对象,然后再从这个对象上面读取方法,这种加载称为“运行时加载”。
commonjs运行时加载

// CommonJS模块
let { stat, exists, readfile } = require('fs');
// 等同于
let _fs = require('fs');
let stat = _fs.stat;
let exists = _fs.exists;
let readfile = _fs.readfile;

es6不是对象,他对外接口只是一种静态定义,在静态编译解析完之后就会完成。即在import时可以指定加载某个输出值,而不是加载整个模块,这种加载称为“编译时加载”

import { stat, exists, readFile } from 'fs';//他只加载了这三个方法

相互引用
ES6模块中,是支持加载CommonJS模块的
但是,CommonJS并不支持requireES6模块

因为CommonJS的require语法是同步的,导致CommonJS只适合用于服务端;而ES6模块在浏览器端和服务器端都是可用的,但是在服务端需要遵循特殊的规则

垃圾回收机制

1、内存泄漏
定义:程序中己动态分配的堆内存由于某种原因程序未释放或无法释放引发的各种问题。
js中可能出现的内存泄漏原因:全局变量,dom 清空时,还存在引用,定时器未清除,子元素存在引起的内存泄露
js的垃圾回收机制是为了防止内存泄漏(已经不需要的某一块内存还一直存在着),垃圾回收机制就是不停歇的寻找这些不再使用的变量,并且释放掉它所指向的内存。
在JS中,JS的执行环境会负责管理代码执行过程中使用的内存。
2.变量的生命周期
当一个变量的生命周期结束之后,它所指向的内存就会被释放。js有两种变量,局部变量和全局变量,局部变量是在他当前的函数中产生作用,当该函数结束之后,该变量内存会被释放,全局变量的话会一直存在,直到浏览器关闭为止。
3.js垃圾回收方式
有两种方式: 标记清除、引用计数
标记清除:大部分浏览器使用这种垃圾回收,当变量进入执行环境(声明变量)的时候,垃圾回收器将该变量进行了标记,当该变量离开环境的时候,将其再度标记,随之进行删除。

引用计数:这种方式常常会引起内存的泄露,主要存在于低版本的浏览器。它的机制就是跟踪某一个值得引用次数,当声明一个变量并且将一个引用类型赋值给变量得时候引用次数加1,当这个变量指向其他一个时引用次数减1当为0时出发回收机制进行回收

函数柯里式

定义:将一个函数的多个参数,分别嵌套到不同函数里面,这些函数由闭包的形式嵌套形成一种链式结构

比如链式法则的加法

add(1)()  //1
add(1)(2)()  //3
add(1)(2)(3)() //6

箭头函数和普通函数区别

箭头函数普通函数
箭头函数的this是在哪里定义函数this指向谁普通函数的this是在哪里调用函数this指向谁(比如setTimeout就是调用的全局变量,所以它指向)
箭头函数没有原型 也不能new 没有构造函数普通函数这些都有
箭头函数没有arguments这个函数普通函数有arguments

this指向问题

分三种情况
1.在全局作用域当中,这时候一般指向window这个全局变量
2.在实例对象当中,那么这里的this,就是指的实例对象里面的值
3.在匿名函数当中,this指向window
4.在普通函数中,this也指向window
5.在箭头函数当中,this指向它被定义的作用域
6.在setTimout中,this指向window
这里有一道经典题目

var name = "小红"

作用域和作用域链

var a = 40 ; 
function fun(){
    var a =20;
    function fn(){
        console.log(a);
    }
    fn()
}
fun()//a=20

//这里的作用域链就是
var a = 40 ; 
 function fn(){
        console.log(a);
    }
function fun(){
    var a =20;
   fn()
}
fun()//a =40

function Person() {
this.name = “张飞”;
return {name: “李白”};
}
var p = new Person();
console.log(p.name);// 李白

function Person() {
this.name = “张飞”;
return [1,2]
}
var p = new Person();
console.log(p, p.name);// [1,2], undefined

//2.return 其他值
function Person() {
this.name = “张飞”;
return “”;
}
var p = new Person();
console.log(p ,p.name);// Persion实例对象, 张飞

//3.return null
function Person() {
this.name = “张飞”;
return null;
}
var p = new Person();
console.log(p ,p.name);// Persion实例对象, 张飞

aysnc 和await Promise

首先是你得知道为什么会有promise这个机制,因为ES5的地狱回调问题,这有一段伪代码

function a(){

}

语法上,promise是一个对象,它可以获取异步操作的消息。(也可以说是一个容器,保存着异步操作的结果)
2.promise对象的特点
(1)对象的状态不受外界影响

Promise对象代表一个异步操作,有三种状态:pending(进行中)、fulfilled(已成功)和rejected(已失败)。
只有异步操作的结果,可以决定当前是哪一种状态,任何其他操作都无法改变这个状态。
(2) 一旦状态改变,就不会再变,任何时候都可以得到这个结果

Promise对象的状态改变,只有两种可能:从pending变为fulfilled和从pending变为rejected。改变之后的状态称为resolved(已定型)。
如果改变已经发生了,你再对Promise对象添加回调函数,也会立即得到这个结果。
3.promise的优缺点
优点:将异步操作以同步操作的流程表达出来,避免了层层嵌套的回调函数(回调地狱问题)。同时,promise对象提供统一的接口,使得控制异步操作更加容易。
缺点:

无法取消Promise,一旦新建它就会立即执行,无法中途取消
如果不设置回调函数,Promise内部抛出的错误,不会反应到外部
当处于pending状态时,无法得知目前进展到哪一个阶段(刚刚开始还是即将完成)

async和await就比较好理解,将异步的代码同步化,例如:

async function fun1(){
	console.log(1)
	let b = await()
}

async funtion fun2(){
	console.log(2)
	let a = 
}

async

https://www.bilibili.com/video/BV13a41187yp/?spm_id_from=333.788&vd_source=a6a8d2478eb3b4cd21d6d7e757b03cad

async和await的错误处理方式

宏任务和微任务

事件循环(Event Loop)

事件执行顺序:
1.同步
2.process.nextTick
3.微任务(promise.then)
4.宏任务(计数器(setTimeout),ajax,读取文件)
5.setImmediate

数组去重

Set() Indexof filter

Map和Set不同

Map和object区别

1、Object的键只能是字符串或者Symbol,而Map的键可以是任意值。
2、Map中的键值是有序的(FIFO),而Object中的键是无序的。
3、Map中的键值个数可以从size属性中获取,而Object中的键值只能手动获取。
Map本质上为二维数组构造

Map和weakmap区别

WeakMap与Map类似,但有几点区别:
1、WeakMap只接受对象作为key,如果设置其他类型的数据作为key,会报错。

2、WeakMap的key所引用的对象都是弱引用,只要对象的其他引用被删除,垃圾回收机制就会释放该对象占用的内存,从而避免内存泄漏。

3、由于WeakMap的成员随时可能被垃圾回收机制回收,成员的数量不稳定,所以没有size属性。

4、没有clear()方法

5、不能遍历

判断数据类型的方法

1.type of
不能判断数组和对象,都返回Object但是可以判断函数 function
2. instance of
可以判断数组类型
3. Object.prototype.string.call()
4. 变量+constructor== 类型名字 比如[].constructot === Array

typeof 和instanceof的几个奇怪的特例

typeof NaN//Number
Object.prototype.call

js的数字精度问题

js是64位双精度浮点数类型 然后他最大的数是21024-1,他的最大安全数位253
安全数的意思是他在这个数之后就是不精确的。

深拷贝和浅拷贝的不同

浅拷贝:只是拷贝数据的内存地址,而不是在内存中重新创建一个一模一样的对象(数组),如果把拷贝的值改变他是可以被改变的(Object.assign)

深拷贝:在内存中开辟一个新的存储空间,完完全全的拷贝一整个一模一样的对象(数组),两个值在改变的时候互不影响。(递归和Json.parse(Json.stringify()))

!!!Json.parse(Json.stringify())的使用弊端:
1.obj里有function,undefined,则序列化的结果会把function或 undefined丢失;
2.obj里面有new Date(),深拷贝后,时间会变成字符串的形式。而不是时间对象;
3.obj里有NaN、Infinity和-Infinity,则序列化的结果会变成null;

b = a.slice(); 如果a数组里的值都是基本数据类型,那就是深拷贝
其次,如果其中有引用数据类型,那拷贝的就是引用数据类型的地址,那么改了a,b对应的引用类型也会改。
最后注意,如果 b[2] = 100, 对应的引用类型 a[2]不会更改,因为 b[2]改的是指针,不是引用数据类型
rest参数也就是([…args])和slice同理

堆内存和栈内存

栈内存:一般存放js的基本数据类型,如String Number Boolean,Symbol
堆内存:一般存放js的引用数据类型,如数组,对象,函数等等 (主要是引用类型的大小是变化的)

为什么const定义的值部分能改,部分不能改
当我们定义一个const对象的时候,我们说的常量其实是指针,就是const对象对应的堆内存指向是不变的,但是堆内存中的数据本身的大小或者属性是可变的。而对于const定义的基础变量而言,这个值就相当于const对象的指针,是不可变。

为什么const、let定义的变量不能二次定义
每次使用const或者let去初始化一个变量的时候,会首先遍历当前的内存栈,看看有没有重名变量,有的话就返回错误。

new关键字初始化的之后是不是存储在栈内存中
根据构造函数生成新实例,这个时候生成的是对象,而不是基本类型。

let str1 = new String(‘123’)

let str2 = new String(‘123’)

console.log(str1str2, str1=str2) // false false

很明显,如果str1 ,str2 是存储在栈内存中的话,两者应该是明显相等的,但结果两者并不相等,说明两者都是存储在堆内存中的,指针指向不一致。

值类型和引用类型其实说的就是栈内存变量和堆内存变量;值传递和引用传递、深拷贝和浅拷贝,都是围绕堆栈内存展开的,一个是处理值,一个是处理指针。

内存分配和垃圾回收
一般来说栈内存线性有序存储,容量小,系统分配效率高。而堆内存首先要在堆内存新分配存储区域,之后又要把指针存储到栈内存中,效率相对就要低一些了。

垃圾回收:栈内存变量基本上用完就回收了,而推内存中的变量因为存在很多不确定的引用,只有当所有调用的变量全部销毁之后才能回收。

为什么基本数据类型放在栈内存

因为栈内存能够存放比较小的

JS的相关数组操作

1.slice截取功能

var aa = [1,2,3,4,5,6];
console.log(aa.slice(2)); //[3,4,5,6]
console.log(aa.slice(2,8)); //[3,4,5,6] 超过最大长度,只显示到最后结果
console.log(aa.slice(2,5)); //[3,4,5]
console.log(aa.slice(2,-1)); //[3,4,5] 相对于倒数第一个之前
console.log(aa.slice(2,-2)); //[3,4] 相对于倒数第二个之前
console.log(aa.slice(3)); //[4,5,6] 一个参数从第三个到最后
console.log(aa.slice(-2));//[5,6] 一个参数负值从倒数第二个到最后

2.pop功能:从数组末尾删除1个元素(删且只删除1个), 并返回被删除的元素

 var arr = [1,2,3,5,6,'a','b','c']
        console.log( arr.pop()) //c
        console.log(arr) //[1, 2, 3, 5, 6, 'a', 'b']

3.join功能:将数组中所有元素都转化为字符串并连接在一起。

        var arr = [1,2,3,5,6,'a','b','c']
        console.log(arr) //
        console.log( arr.join('')) //12356abc

4.filter功能筛选

var arr = [1, 2, 3, 4, 5, 6, 7, 8, 9]
var newArray = arr.filter((item) => {
    return item > 5
})
console.log(arr);// [1,2,3,4,5,6,7,8,9] 不会修改原数组
console.log(newArray);//[6, 7, 8, 9] 返回新数组

5.splice功能:
1.删除:从数组中删除元素,两个参数,第一个参数(要删除元素的起始位置),第二个参数(要删除的项数)
2.添加:插入元素到数组中,三个参数,第一个参数(起始位置),第二个参数(0),第三个参数(插入的项数)
3.替换:删除、插入元素同时完成
输出:返回一个由删除元素组成的数组。
注意:新建了一个数组,并修改了原数组

判断空对象

1.使用Json.stringify

function isEmptyObj(obj) {
    return JSON.stringify(obj) === '{}'
}
console.log('对象是否为空:', isEmptyObj({}))

2.for in

function isEmptyObj(obj) {
    for(let item in obj) {
        return true
    }
    return false
}    
console.log('对象是否为空:', isEmptyObj({}))

3.Object.key()

function isEmptyObj(obj) {
    return Object.keys(obj).length === 0
}
console.log('对象是否为空:', isEmptyObj({}))

js中改变和不改变数组的方法

原数组改变的方法有:shift unshift reverse sort push pop splice

不改变原数组的方法有:concat map filter join every some indexOf slice forEach

indexOf()

方法可返回数组中某个指定的元素位置。
该方法将从头到尾地检索数组,看它是否含有对应的元素。开始检索的位置在数组 start 处或数组的开头(没有指定 start参数时)。如果找到一个 item,则返回 item 的第一次出现的位置。

Map和Foreach

2、不同点
(1)map()会分配内存空间存储新数组并返回,forEach()不会返回数据。
(2)forEach()允许callback更改原始数组的元素。map()返回新的数组。

Map和Object

1、Object的键只能是字符串或者Symbol,而Map的键可以是任意值。

2、Map中的键值是有序的(FIFO),而Object中的键是无序的。

3、Map中的键值个数可以从size属性中获取,而Object中的键值只能手动获取。

map和weakmap的区别

我们可以对 Map 的键和值使用对象或任何基本类型。
但是,WeakMap 仅接受对象。这意味着我们不能将基本类型用作 WeakMap 的键。
由于 WeakMap 保存对键的弱引用,且无法枚举,因此**无法使用 keys()、values()、entries() **这些方法。
WeakMap的key所引用的对象都是弱引用,只要对象的其他引用被删除,垃圾回收机制就会释放该对象占用的内存,从而避免内存泄漏
3、由于WeakMap的成员随时可能被垃圾回收机制回收,成员的数量不稳定,所以没有size属性
4、没有clear()方法
5、不能遍历

Number和ParseInt区别

Number() ,从翻译中看语义,number就是数字的之意。
它意味着无论给Number()传入任何参数,它可以把内容转换为数字

parseInt(),在英语上,分析词句之意,而 Int 则是我们定义变量时的整数类型。
显而易见,则是通过分析把词句转换为整型。
而在JS中,我们词句的数据类型,就是我们常见的string字符串
因此,parseInt()就是把字符串转换为整型之意

判断数组类型

1.上面的方法都可以(instance of )
2.Array.isarray()

JS判断数组类型的方法
1、 Array.isArry()方法 ,返回布尔值

console.log(Array.isArray([]))

2、 instanceof方法 ,返回布尔值

console.log([] instanceof Array)

3、 constructor方法 判断

console.log([].constructor == Array)

4、 使用Object.prototype.toString()方法 判断

function isArray(arr){
  return Object.prototype.toString.call(arr) == "[object Array]" 
}
console.log(isArray([]))

Object.prototype.toString的内部实现,应该说一下会对非null 或 undefined的数据类型进行装箱操作,然后去找出对象的 【Symbol.toStringTag】 属性值,还有可能要遍历原型链,取到的值作为 tag, 然后返回 "【object " + tag + “】” 形式的字符串.其实每个类别都toString()的功能,当我们把toString删除了之后就能返回[object + 属性值]

typeof 原理
typeof原理: 不同的对象在底层都表示为二进制,在Javascript中二进制前(低)三位存储其类型信息。
000: 对象
010: 浮点数
100:字符串
110: 布尔
1: 整数

var let const区别

在这之前我们得先了解一下块级作用域。
ES5 中作用域有:全局作用域、函数作用域。没有块作用域的概念。
ES6 中新增了块级作用域。块作用域由 { } 包括,if语句和 for语句里面的{ }也属于块作用域。

var是没有块的概念,可以跨块访问,不能跨函数访问
let只能在块的作用域里面进行访问,不能跨函数访问
const必须有定义的值(初始化值),并且他也只能在块作用域里访问,而且不能修改。

  {
        var a = 1;
        console.log(a); // 1
    }
    console.log(a); // 1
    // 通过var定义的变量可以跨块作用域访问到。
 // 函数作用域
    (function A() {
        var d = 5;
        let e = 6;
        const f = 7;
        console.log(d); // 5
        console.log(e); // 6  
        console.log(f); // 7 
 
    })();
    // console.log(d); // 报错
    // console.log(e); // 报错
    // console.log(f); // 报错
var可以先使用,后声明,因为存在变量提升;let必须先声明后使用。
var是允许在相同作用域内重复声明同一个变量的,而let与const不允许这一现象。
在全局上下文中,基于let声明的全局变量和全局对象GO(window)没有任何关系 ;
var声明的变量会和GO有映射关系;
会产生暂时性死区:

var和let区别:第一个let不能跨块级作用域使用,var有变量提升而let没有,let会出现暂时性死区
let和const区别:let可以重新赋值,const不行,let可以不声明变量,const必须先申明变量 两个都不能跨块级作用域

3.2 节流和防抖

节流:设定一定的阈值,能够使他限制多次点击的次数,避免资源浪费
防抖:它指的是在多次点击的 情况下,能够做到只在最后一次进行点击

addEventListener

事件冒泡和事件捕捉

总结:事件流的模型是自上而下捕获,到达目标,然后再自下而上冒泡。
1.事件捕获阶段
2.处于目标阶段
3.事件冒泡阶段

对于dom0级btn.onclick
dom2级btn.addEventListener(‘click’,fn,false)
都是默认事件冒泡。其中dom2级第三个默认false,改为true就变为事件捕获。
事件捕获大于事件冒泡
这里有有一道经典题目

js 阻塞问题

首先是浏览器渲染引擎和js渲染引擎的冲突问题
浏览器渲染机制

1、浏览器根据服务器响应回来的html,进行解析后构建一颗DOM节点树;
2、根据css文件,构建得到CSSOM树;
3、将DOM树与CSSOM树结合,精确计算每个节点的位置、尺寸等等属性,构建出渲染树;
4、渲染至用户页面

而浏览器引擎分为渲染引擎以及js引擎,其实之前是区分这2个概念的,但是慢慢地大家发现有必要将二者进行独立区分
1、gui渲染引擎:主要负责html解析以及css等相关文件
2、js引擎:负责js代码运行与解析

js阻塞问题是指当浏览器在解析文档或者渲染页面时,遇见了js代码,需要渲染引擎中断,而运行js引擎,从而阻塞浏览器原本的工作状态。

1、script标签放在头部加载:由于需要加载js代码,所以会阻塞后面html的解析与加载;
2、放在中间与放在头部道理一样;
3、放在底部(body闭合之前):此时由于DOM已经加载解析完了,浏览器可以专心地加载js代码

defer和async区别

defer(推迟):他和异步的区别就是时间出去的是有一定的顺序的,在执行这段 JavaScript 加载时, HTML 并未停止解析,这两个过程是并行的。当整个 document 解析完毕后再执行脚本文件,在 DOMContentLoaded 事件触发之前完成。多个脚本按顺序执行。
async(异步):同步如果是单通道进行任务,那么异步就是两条道同时进行任务,但是出口只有一个,事件的出去的顺序不知道。与 defer 的区别在于如果已经加载好,就会开始执行,也就是说它的执行仍然会阻塞文档的解析,只是它的加载过程不会阻塞。多个脚本的执行顺序无法保证。

css会阻塞dom渲染嘛

CSS不会阻塞DOM解析,但是会阻塞DOM渲染,严谨一点则是CSS会阻塞render tree的生成,进而会阻塞DOM的渲染
JS会阻塞DOM解析
CSS会阻塞JS的执行
浏览器遇到

css会影响dom渲染吗?

会,但是不会影响解析
1、CSS不会阻塞DOM树解析
2、CSS加载会阻塞DOM树渲染
3、CSS加载会阻塞后面JS语句的执行

函数提升与变量提升的优先级

函数提升优先级高于变量提升,且不会被同名变量声明时覆盖,但是会被同名变量赋值后覆盖

 function a(){}  // 函数声明提升 a-> f a (){}
 var a;        // 变量提升
 console.log(a)  // 此时变量a只是声明没有赋值所以不会覆盖函数a --> 输出函数a  f a (){}
 a=1;     //变量赋值
 console.log(a)  // 此时变量a赋值了 --> 输出变量a的值 1

还有一种情况

 function a(){}  // 函数声明提升 a-> f a (){}
 var a;        // 变量提升
 console.log(a)  // 此时变量a只是声明没有赋值所以不会覆盖函数a --> 输出函数a  f a (){}
 a=1;     //变量赋值
 console.log(a)  // 此时变量a赋值了 --> 输出变量a的值 1

避免白屏的出现


3.3 作用域和作用域链

var a = 40 ; 
function fun(){
    var a =20;
    function fn(){
        console.log(a);
    }
    fn()
}
fun()//a=20

//这里的作用域链就是
var a = 40 ; 
 function fn(){
        console.log(a);
    }
function fun(){
    var a =20;
   fn()
}
fun()//a =40

原型和原型链

原型的定义:一个可以被复制(或者叫克隆)的一个类,通过复制原型可以创建一个一模一样的新对象,也可以说原型就是一个模板,在设计语言中更准确的说是一个对象模板。
原型链的定义:原型链是原型对象创建过程的历史记录,当访问一个对象的某个属性时,会先在这个对象本身属性上查找,如果没有找到,则会去它的__proto__隐式原型上查找,即它的构造函数的prototype,如果还没有找到就会再在构造函数的prototype的__proto__中查找,这样一层一层向上查找就会形成一个链式结构。

hasOwnProperty 函数:可以用来检查对象自身是否含有某个属性,返回值是布尔值,当属性不存在时不会向上查找对象原型链,hasOwnProperty是 JavaScript 中唯一一个处理属性但是不查找原型链的函数

apply call bind

说完this问题就可以考虑改变this的问题了
apply和call都差不多

js找最大值

最大值
Math.max.apply(null,arr)
最小值
Math.min.apply(null,arr)

继承方式

1.原型链继承

function Mother(){
	this.name ="张三"
	this.money = [1,2,3,4]
}


function GrandMother(){}
var son  =new Mother()

2.构造函数继承
3.组合继承
4.组合寄生继承
5.寄生继承
6.原型式继承

js设计模式

https://blog.csdn.net/woyebuzhidao321/article/details/120235389

1.工厂模式


2.单例模式

只能被实例化一次,之后的实例化都和第一次实例化相同

function Person(name.age){
	this.name = name;
	this.age =age;
}

Person.prototype.sayHello = function(){
		console.log(`My name is ${this.name}`)
}
let PersonProxy = (function(){
	let instance =null
	return function(name,age){
		if(instance){
		return instance
			}
		instance = new Person(name,age)
}
})()

let one = new PersonProxy("fw",16)
let  two= new PersonProxy("fw",18)

3.代理模式

function Mx(){
	this.sing = function(){
		console.log( "可以开演唱会..." );
	}
}

function Jjr(){
	this.sing = function(money){
		if( money >= 200000000 ){
			//通知主人 可以开演唱会啦
			new Mx().sing();
		}
	}
}

var jj = new Jjr();
jj.sing( 200000001 );

4.适配器模式

5.策略模式

6.发布订阅模式和观察者模式

7.观察者模式

onload和ready的区别

document.ready:是DOM结构绘制完毕后就执行,不必等到加载完毕。意思就是DOM树加载完毕就执行,不必等到页面中图片或其他外部文件都加载完毕。

onload:是页面所有元素都加载完毕,包括图片等所有元素。

Dom的增删改查

1.增

document.appendChild()
document.setAttribute()

2.删

document.removeChild()

3.改

docment

4.查

document.getElementById()
document.get

requestAnimationFrame和setInterval、setTimeout的区别

上边提到可以理解是一个定时器,大家就肯定想到了setInterval、setTimeout,他们有什么区别?

队列执行优先级不同:三者都是是异步 api,setTimeout 和 setInterval属于宏任务; requestAnimationFrame属于“渲染任务”(调用GUI 引擎),执行优先级在宏任务前,微任务之后。
//每个tick执行逻辑如下:
…->上一个宏任务 -> 微任务(下一个宏任务前的所有微任务) -> 渲染任务 -> 下一个宏任务 ->…
代码测试如下:

setTimeout(function(){
console.log(“我是setTimeout”, new Date().getTime())
},5)

requestAnimationFrame(function(){ //浏览器刷新频率是16.7ms左右,远大于5ms
console.log(“我是requestAnimationFrame”, new Date().getTime());
})

new Promise(function(resolve){
resolve(‘我是微任务’)
}).then(res=>{
console.log(res, new Date().getTime());
})
//下边是打印信息:

//我是微任务 1662356658402
//我是requestAnimationFrame 1662356658403
//我是setTimeout 1662356658408

setTimeout写在requestAnimationFrame前,却在requestAnimationFrame后执行,而且通过打印时间戳可以看到相隔时间就是5ms左右,就是说明在执行requestAnimationFrame 后setTimeout才开始执行

典型的 MacroTask(宏任务) 包含了 setTimeout, setInterval, setImmediate, requestAnimationFrame, I/O, UI rendering等;
MicroTask(微任务)包含了 process.nextTick, Promises, Object.observe, MutationObserver等。

定时时间不同:setTimeout 和 setInterval的定时时间是我们自己定的,而requestAnimationFrame的定时时间是浏览器的刷新间隔时间(一般浏览器1s刷新60次,刷新间隔时间大约为16.7s,所以不同浏览器刷新频率不同会导致定时时间不同),不用自己计算渲染时间,而且不会掉帧卡顿

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值