深浅拷贝
基本数据类型
number、boolean、string、null、undefined、symbol
基本数据类型是以名值得形式存储在栈内存中的。
进行赋值操作时会新开辟一片栈内存存储新的名值。
引用数据类型
数组、对象、函数等
以上的类型会在栈内存和堆内存中分别开辟一片空间进行存储。
当直接赋值时其实是将a的堆地址赋值给了b,两者最终指向了同一个堆内存。这就是浅拷贝,带来的后果就是你在对b的元素进行操作时,同时改变了a对应的值。
深拷贝方法
方法一
第一种方法简单粗暴,使用JSON.stringify和JSON.parse进行两次转换。
let a = [1,3,5,7];
let b = JSON.parse(JSON.stringify(a));
b[0] = 99;
console.log("a", a); // [1,3,5,7]
console.log("b", b); // [99,3,5,7]
这种方法存在一种很大的缺点,转换时会自动忽略undefined,Symbol、function
方法二
用递归的方法去遍历复制所有的层级
function deepClone(obj){
// 判断数据形式
let clone = Array.isArray(obj)?[]:{};
if(obj && typeof obj === "object"){
for(key in obj){
if(obj.hasOwnProperty(key)){
// 属性是对象则进行递归
if(obj[key] && typeof obj[key] === "object"){
clone[key] = deepClone(obj[key]);
}else{
clone[key] = obj[key];
}
}
}
}
return clone;
}
let a = [1,3,5,7],
b = deepClone(a);
a[0] = 999;
console.log(a); // [1,3,5,7]
console.log(b); // [999,3,5,7]
//写一个函数
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)
}
数组去重
var arr = [1, 1, "true", "true", true, 15, false]
//利用ES6去重
function unique(arr) {
return Array.from(new Set(arr))
}
//利用indexOf方法(该方法用于返回指定数组元素首次出现的地方,若没有则返回-1)
function unique(arr) {
if(!Array.isArray(arr)) {
console.log("error");
return ;
}
let result = [];
for (let i = 0; i < arr.length; i ++) {
if (result.indexOf(arr[i]) == -1) {
result.push(arr[i])
}
}
return result;
}
//利用对象属性不能相等
function unique(arr) {
if (! Array.isArray(arr)) {
return
}
let result = [];
let obj = {};
for (let i = 0; i < arr.length; i ++) {
if (!obj[arr[i]]) {
result.push(arr[i])
obj[arr[i]] = 1;
} else {
obj[arr[i]] ++
}
}
return array;
}
实现sleep的效果
//使用Promise 先输出111,延迟500ms后输出222
function sleep(ms) {
return new Promise((resolve) => {
console.log("111");
setTimeout(resolve,ms)
})
}
sleep(500).then(function(){
console.log("222")
})
//使用async/await 过了500ms后才输出111
function sleep(ms) {
return new Promise((resolve) => {
setTimeout(resolve,ms)
})
}
async function test() {
let temp = await sleep(500);
console.log("111");
return temp;
}
实现Promise
class PromiseM {
constructor(process) {
this.status = "pending";
this.msg = "";
process(this.resolve.bind(this),this.reject.bind(this))
return this;
}
resolve(val) {
this.status = "fulfilled";
this.msg = val;
}
reject(val) {
this.status = "rejected";
this.msg = err;
}
then(fulfilled,reject) {
if (this.status === "fulfilled") {
fulfilled(this.msg)
}
if (this.status === "rejected") {
reject(this.msg)
}
}
}
类的创建
new一个function,在这个function的prototype里面增加属性和方法。
// 定义一个动物类
function Animal (name) {
// 属性
this.name = name || 'Animal';
// 实例方法
this.sleep = function(){
console.log(this.name + '正在睡觉!');
}
}
// 原型方法
Animal.prototype.eat = function(food) {
console.log(this.name + '正在吃:' + food);
};
//这样就生成了一个Animal类,实力化生成对象后,有方法和属性。
类的继承
//1. 原型链继承,将父类的实例作为子类的原型
function Cat() {
}
Cat.prototype = new Animal();
Cat.prototype.name = 'cat';
var cat = new Cat;
console.log(cat.name);
console.log(cat.eat('fish'));
console.log(cat.sleep());
console.log(cat instanceof Animal); //true
console.log(cat instanceof Cat); //true
//特点:
//非常纯粹的继承关系,实例是子类的实例,也是父类的实例
//父类新增原型方法/原型属性,子类都能访问到
//简单,易于实现
//缺点:
//要想为子类新增属性和方法,必须要在new Animal()这样的语句之后执行,不能放到构造器中
//无法实现多继承
//来自原型对象的所有属性被所有实例共享(来自原型对象的引用属性是所有实例共享的)
//创建子类实例时,无法向父类构造函数传参
//2. 构造继承,使用父类的构造函数来增强子类实例,等于是复制父类的实例属性给子类
function Cat(name) {
Animal.call(this);
this.name = name || 'Tom';
}
console.log(cat instanceof Animal); //false
console.log(cat instanceof Cat); //true
//特点:
//解决了1中,子类实例共享父类引用属性的问题
//创建子类实例时,可以向父类传递参数
//可以实现多继承(call多个父类对象)
//缺点:
//实例并不是父类的实例,只是子类的实例
//只能继承父类的实例属性和方法,不能继承原型属性/方法
//无法实现函数复用,每个子类都有父类实例函数的副本,影响性能
// 3. 实例继承 为父类实例添加新特性,作为子类实例返回
function Cat(name) {
var instance = new Animal();
instance.name = name || 'Tom';
return instance
}
console.log(cat instanceof Animal); //true
console.log(cat instanceof Cat); //false
//特点:
//不限制调用方式,不管是new 子类()还是子类(),返回的对象具有相同的效果
//缺点:
//实例是父类的实例,不是子类的实例
//不支持多继承
//4. 组合继承 通过调用父类构造,继承父类的属性并保留传参的优点
//然后通过将父类作为子类原型,实现函数复用
function Cat(name) {
Animal.call(this);
this.name = name || 'Tom';
}
Cat.prototype = new Animal();
Cat.prototype.constructor = Cat;
console.log(cat instanceof Animal); //true
console.log(cat insertSort Cat); //true
//特点:
//弥补了方式2的缺陷,可以继承实例属性/方法,也可以继承原型属性/方法
//既是子类的实例,也是父类的实例
//不存在引用属性共享问题
//可传参
//函数可复用
//缺点:
//调用了两次父类构造函数,生成了两份实例
//5. 寄生组合继承
//通过寄生方式,砍掉父类的实例属性,
//这样,在调用两次父类的构造的时候,就不会初始化两次实例方法/属性
function Cat(name) {
Animal.call(this);
this.name = name || 'Tom';
}
(function() {
var Super = function(){};
Super.prototype = Animal.prototype;
Cat.prototype = new Super()
})()
console.log(cat instanceof Animal); // true
console.log(cat instanceof Cat); //true
//特点:
//堪称完美
//缺点:
//实现较为复杂
链表
class Node{
constructor(data) {
this.data = data;
this.next = null;
}
}
class LinkList{
constructor() {
this.head = new Node('head');
}
findByValue = (value) => {
let currentNode = this.head;
while (currentNode !== null && currentNode.data !== value) {
currentNode = currentNode.next;
}
return currentNode === null ? -1 : currentNode;
}
findByIndex = (index) => {
let pos = 0;
let currentNode = this.head;
while (currentNode !== null && pos !== index) {
pos ++;
currentNode = currentNode.next;
}
return currentNode === null ? -1 : currentNode;
}
insert = (value, element) => {
let currentNode = this.findByValue(element);
if (currentNode == -1) {
console.log('error');
return;
}
let newNode = new Node(value);
newNode.next = currentNode.next;
currentNode.next = newNode;
}
delete = (value) {
let currentNode = this.head;
preNode = null;
while (currentNode !== null && currentNode.data !== value) {
preNode = currentNode;
currentNode = currentNode.next;
}
if (currentNode == null) {
return -1
}
preNode.next = currentNode.next;
}
print = () => {
let currentNode = this.head
while (currentNode !== null) {
console.log(currentNode.data)
currentNode = currentNode.next;
}
}
}
JS控制一次加载一张图片, 加载完后再加载下一张
https://blog.csdn.net/weixin_44462907/article/details/88756890
let loadImg = (src) => {
return new Promise((resolve, reject) => {
let img = document.createElement('img')
img.src = src
document.body.append(img)
setTimeout(() => {
resolve(true)
}, 1000)
})
}
const imgs = ['../src/img/1.PNG', '../src/img/1.PNG', '../src/img/1.PNG'];
// 依次加载图片
async function fSync(imgs) {
for (let i of imgs) {
await loadImg(i)
}
}
fSync(imgs);
js防抖 节流
防抖:
对于短时间内连续触发的事件(如滚动事件),防抖的含义就是让某个时间期限(如上面的1000毫秒)内,事件处理函数只执行一次。
防抖实现思路:在第一次触发事件时,不立即执行函数,而是给出一个期限值比如200ms,然后:
- 如果在200ms内没有再次触发滚动事件,那么就执行函数
- 如果在200ms内再次触发滚动事件,那么当前的计时取消,重新开始计时
效果:如果短时间内大量触发同一事件,只会执行一次函数。
实现:既然前面都提到了计时,那实现的关键就在于setTimeOut
这个函数,由于还需要一个变量来保存计时,考虑维护全局纯净,可以借助闭包来实现:
/*
* fn [function] 需要防抖的函数
* delay [number] 毫秒,防抖期限值
*/
function debounce(fn, delay) {
let timer = null;
return function() {
if (timer) {
claerTimeout(timer)
}
timer = setTimeout(fn, delay)
}
}
节流
效果:如果短时间内大量触发同一事件,那么在函数执行一次之后,该函数在指定的时间期限内不再工作,直至过了这段时间才重新生效
function throttle(fn, delay) {
let valid = true;
return function() {
if (!valid) { //valid = false,休息时间,不执行
return false
}
valid = false
setTimeout(() => {
fn()
valid = true;
}, delay)
}
}
基于promise实现jsonp
function p(url){
let json;
let s = document.createElement('script');
s.src = url + '?callback=fn';
window.fn = function(data){
json = data;
}
//当script被插入文档中时,src中的资源就会开始加载
document.body.appendChild(s);
return new Promise((resolve,reject)=>{
/* throw('err in promise'); */
s.onload = function(e){
resolve(json);
}
s.onerror = function(){
reject(json);
}
});
}
p('http://localhost:8082').then(data=>{
console.log(data);
throw('err before then');
}).catch(err => {
//可以捕捉到then里的err befor then也可以捕捉到new Promise里的err in promise。
console.log(err)
new一个对象中间做了什么操作
- new 操作符新建了一个空对象
- 这个对象原型指向构造函数的prototype
- 执行构造函数后返回这个对象。
自己实现new函数
function create(){
//创建一个空对象
let obj = new Object();
//1.拿到传入的参数中的第一个参数,即构造函数名Func
let Constructor = [].shift.call(arguments);
//链接到原型
obj.__proto__ = Constructor.prototype;
//绑定this值
let result = Constructor.apply(obj,arguments);//使用apply,将构造函数中的this指向新对象,这样新对象就可以访问构造函数中的属性和方法
//返回新对象
return typeof result === "object" ? result : obj;//如果返回值是一个对象就返回该对象,否则返回构造函数的一个实例对象
}
如何判断一个对象是不是空对象?
//for...in...遍历属性
function judgeObj(obj) {
for (var attr in obj) {
return alert("非空对象")
}
return alert("空对象")
}
//JSON自带的stringify()方法
if(JSON.stringify(obj) == "{}") {
console.log("空对象")
}
//ES6新增的方法Object.keys()
if(Object.keys(obj).length > 0) {//会转化为一个数组
console.log("非空对象")
}
async和generator有什么区别,写一个async和generator函数,并介绍区别
async
函数是Generator
函数的语法糖,将Generator
的星号换成async
,将yield
换成await
,async
函数比Generator
函数更好用.
Generator
与async function
都是返回一个特定类型的对象:
Generator
: 一个类似{ value: XXX, done: true }
这样结构的Object
Async
: 始终返回一个Promise
,使用await
或者.then()
来获取返回值
Generator
是属于生成器,一种特殊的迭代器,用来解决异步回调问题感觉有些不务正业了。。 而async
则是为了更简洁的使用Promise
而提出的语法,
//Generator
function * oddGenerator () {
2 yield 1
3 yield 3
4
5 return 5
6 }
7
8 let iterator = oddGenerator()
9
10 let first = iterator.next() // { value: 1, done: false }
11 let second = iterator.next() // { value: 3, done: false }
12 let third = iterator.next() // { value: 5, done: true }
//async
1 function getRandom () {
2 return new Promise(resolve => {
3 setTimeout(_ => resolve(Math.random() * 10 | 0), 1000)
4 })
5 }
6
7 async function main () {
8 let num1 = await getRandom()
9 let num2 = await getRandom()
10
11 return num1 + num2
12 }
用promise和async实现每间隔1s,2s,3s…打印i
function sleep(interval){
return new Promise((resolve)=>
setTimeout(resolve, interval);
});
}
async function stepPrint(n){
for(let i=0;i<=n;i++){
console.log(i);
await sleep(i*10000);
}
}
stepPrint(5)
手写bind
Function.prototype._bind = function(){
var self = this // 保存原函数
var context = [].shift.call(arguments) // 保存需要绑定的this上下文
var args = [].slice.call(arguments) // 剩余的参数转为数组
return function(){// 返回一个新函数
self.apply(context, args.concat([].slice.call(arguments)))
}
}
https://blog.csdn.net/lovefengruoqing/article/details/80186401
循环对象的方法有哪几种以及它们的区别
https://www.cnblogs.com/nerrol/p/8137065.html
检测数据类型的方法有哪几种
https://www.cnblogs.com/MrZhujl/p/9837152.html
数组的typeof是什么,如何判断数组,几种方法
Object.prototype.toString.call(arg)==='[object Array]'
https://blog.csdn.net/weixin_33744854/article/details/91370655
将原生的ajax封装成promise
var myNewAjax=function(url) {
return new Promise(function(resolve,reject){
var xhr = new XMLHttpRequest();
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');
}
}
})
}
如何实现一个私有变量,用getName方法可以访问,不能直接访问
function People(){
25 var name='张三'; // 私有变量; 外部无法访问
27 function say(){ // 私有函数;
28 return '我是......';
29 }
31 this.getName=function(){ // 对外公共的特权方法; 外部可以访问
32 return name;
33 }
38 }
39 var p=new People()
40 alert(p.getname())
怎么获得对象上的属性:比如说通过Object.keys()
- 对象内置属性方法:Object.keys();该方法返回一个数组,数组内包括对象内可枚举属性以及方法名称。
var keys= Object.keys(testObj);
console.log(keys); // 输出 keys ["name", "age", "action"]
2.Object.getOwnPropertyNames():
方法返回一个指定对象所有自身属性的属性名(包括不可枚举属性但不包括Symbol值作为名称的属性)组成的数组.(不可枚举: 比如属性是一个js方法)
var keys = Object.getOwnPropertyNames(testObj);
console.log(keys); // 输出 keys ["name", "age", "action"]
3.最原始的 for…in 循环。
var keys =[];
for(var i in testObj){
keys.push(i);
}
console.log(keys); // keys ["name", "age", "action"]
同时并发请求如何保证返回有序
回调
只适合并发数少的情况,多层嵌套回调会让代码的可读性大大降低
ajax改为同步
如在jquery中将async参数设置为false
Promise
async/await
https://www.jb51.net/article/74018.htm
JavaScript中的轮播实现原理
<!DOCTYPE html>
<html>
<head>
<title>Test</title>
</head>
<body>
<div class="imgBox">
<img class="img-slide img1" src="images/1.png" alt="1">
<img class="img-slice img2" src="images/2.png" alt="2">
<img class="img-slice img3" src="images/3.png" alt="3">
</div>
<style>
.img1{
display:block;
}
.img2{
display: none;
}
.img3{
display: none;
}
</style>
<script type="text/javascript">
var index = 0;
function changeImg() {
index ++;
var a = document.getElementsByClassName("img-slice");
if (index >= a.length) {
index = 0;
}
for (let i = 0; i < a.length; i ++) {
a[i].style.display = "none";
}
a[index].style.display = "block";
}
setInterval(changeImg, 2000)
</script>
</body>
</html>
https://www.jianshu.com/p/366e374e108d
JS的垃圾回收机制 两种的区别 为啥引用计数不常用
垃圾收集器会定期(周期性)找出那些不在继续使用的变量,然后释放其内存。
标记清除法
在函数声明一个变量的时候,就将这个变量标记为“进入环境”。从逻辑上讲,永远都不能释放进入环境的变量作占用的内存,因为只要执行流进入相应的环境,就可能会用到它们。而当变量离开环境时,则将其标记为“离开环境”。垃圾回收器在运行时候会给存储在内存中中的所有变量都加上标记。然后它会去掉环境中的变量以及被环境中的变量引用的变量的标记(闭包)。在此之后再被标记的变量将被视为准备删除的变量,原因是环境中的变量已经无法访问到这些变量了。最后,垃圾回收器完成内存清除工作,销毁那些带标记的值并回收他们所占用的内存空间。
引用计数法
引用计数的含义是跟踪记录每个值被引用的次数。当声明了一个变量并将一个引用类型值赋给该变量时,则这个值的引用次数就是1。如果同一个值又被赋给另一个变量,则该值的引用次数加1。相反,如果包含对这个值引用的变量又取得了另外一个值,则这个值的引用次数减1。当这个值的引用次数变成0时,则说明没有办法再访问这个值了,因而就可以将其占用的内存空间回收回来。这样,当垃圾回收器下次再运行时,它就会释放那些引用次数为0的值所占用的内存。
但是很重要的一点是当遇到循环引用的时候,函数的引用次数就不会为0,所以不会被垃圾回收器回收内存,会造成内存泄露。在IE中涉及COM对象,就会存在循环引用的问题。
https://blog.csdn.net/qq_21325977/article/details/79973761
bind,apply,call
Call和apply的作用是一模一样的,只是传参的形式有区别而已
1、改变函数体内部this的指向
2、借用别的对象的方法,
3、调用函数,因为apply,call方法会使函数立即执行
https://segmentfault.com/a/1190000018270750
https://www.cnblogs.com/humin/p/4556820.html
定义一个log方法
function log(){
console.log.apply(console, arguments);
};
log(1); //1
log(1,2); //1 2
内存泄漏
https://www.cnblogs.com/libin-1/p/6013490.html
http://www.fly63.com/article/detial/225?type=2
https://blog.csdn.net/qappleh/article/details/80337630
https://segmentfault.com/a/1190000004896090
转换
https://www.cnblogs.com/chenmeng0818/p/5954215.html
如何保证表单安全!!
https://blog.csdn.net/weixin_33769125/article/details/88626742
防止表单重复提交
https://www.cnblogs.com/huanghuizhou/p/9153837.html
https://www.jb51.net/article/41825.htm
事件委托
https://www.jianshu.com/p/c3c98c71e34b
https://www.cnblogs.com/xxflz/p/10588344.html
事件轮询
http://www.fly63.com/article/detial/5355?type=2
fetch API与传统request的区别
https://www.cnblogs.com/iifeng/p/11199058.html
https://www.jianshu.com/p/c7b67a0b67da
数组方法 哪些返回新数组 哪些改变数组‘
https://blog.csdn.net/weixin_46124214/article/details/104334640
addEventListener参数
addEventListener(event, function, useCapture)
其中,event指定事件名;function指定要事件触发时执行的函数;useCapture指定事件是否在捕获或冒泡阶段执行。
作用域不同sessionStorage:不在不同的浏览器窗口中共享,即使是同一个页面;localStorage:在所有同源窗口都是共享的;cookie:也是在所有同源窗口中共享的
js监听对象属性的改变
http://www.zhangchen915.com/index.php/archives/581/
事件模型常用方法:
event.stopPropagation:阻止捕获和冒泡阶段中,当前事件的进一步传播,
event.preventDefault,取消该事件(假如事件是可取消的)而不停止事件的进一步传播,
event.target:指向触发事件的元素,在事件冒泡过程中这个值不变
Ajax解决浏览器缓存问题
禁止浏览器缓存功能有如下几种方法:
在ajax发送请求前加上 anyAjaxObj.setRequestHeader("If-Modified-Since","0")
。
在ajax发送请求前加上 anyAjaxObj.setRequestHeader("Cache-Control","no-cache")
。
在URL后面加上一个随机数:"fresh=" + Math.random();
。
在URL后面加上时间搓:"nowtime=" + new Date().getTime();
。
如果是使用jQuery,直接这样就可以了$.ajaxSetup({cache:false})
。这样页面的所有ajax都会执行这条语句就是不需要保存缓存记录。
js拖拽功能的实现
在允许拖拽的节点元素上,使用on来监听mousedown(按下鼠标按钮)事件,鼠标按下后,克隆当前节点
监听mousemove(鼠标移动)事件,修改克隆出来的节点的坐标,实现节点跟随鼠标的效果
监听mouseup(放开鼠标按钮)事件,将原节点克隆到鼠标放下位置的容器里,删除原节点,拖拽完成。
拖拽的基本原理就是根据鼠标的移动来移动被拖拽的元素。鼠标的移动也就是x、y坐标的变化;元素的移动就是style.position的top和left的改变。当然,并不是任何时候移动鼠标都要造成元素的移动,而应该判断鼠标左键的状态是否为按下状态,是否是在可拖拽的元素上按下的。
https://www.cnblogs.com/psxiao/p/11547834.html
实现一个tab组件
https://blog.csdn.net/qq_23244029/article/details/93517575
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width">
<title>实现Tab组件</title>
<link rel="stylesheet" type="text/css" href="tab.css">
</head>
<body>
<div id="container1">
<ul class="tab-list">
<li class="tab-item" href="panel-1">tab1</li>
<li class="tab-item" href="panel-2">tab2</li>
<li class="tab-item" href="panel-3">tab3</li>
</ul>
<div class="panel active" id="panel-1">content1</div>
<div class="panel" id="panel-2">content2</div>
<div class="panel" id="panel-3">content3</div>
</div>
<script>
/**
* Tab 组件
* @param {String} containerId 容器Id
*/
function Tab(containerId)
{
var cont1 = document.getElementById(containerId);
var tab_items = cont1.getElementsByClassName('tab-item');
var pls = cont1.getElementsByClassName('panel');
for(let i = 0;i < tab_items.length;i++){
tab_items[i].onclick = function(e){
console.log(e);
for(let j = 0;j<tab_items.length;j++){
if(i == j){
pls[i].style.display = "block";
}else{
pls[j].style.display = "none";
}
}
}
}
}
/**
* active 方法,可以控制第几个 Tab 组件显示
* @param {Number} index 要显示的 tab 的序号,从0开始
*/
Tab.prototype.active = function (index)
{
}
var tab = new Tab('container1');
</script>
</body>
</html>
写一个观察者模式
写一个发布订阅模式
https://www.jianshu.com/p/e0575e17de2a
https://www.cnblogs.com/suyuanli/p/9655699.html
// 发布订阅模式
class EventEmitter {
constructor() {
// 事件对象,存放订阅的名字和事件
this.events = {};
}
// 订阅事件的方法
on(eventName,callback) {
if (!this.events[eventName]) {
// 注意时数据,一个名字可以订阅多个事件函数
this.events[eventName] = [callback]
} else {
// 存在则push到指定数组的尾部保存
this.events[eventName].push(callback)
}
}
// 触发事件的方法
emit(eventName) {
// 遍历执行所有订阅的事件
this.events[eventName] && this.events[eventName].forEach(cb => cb());
}
// 移除订阅事件
removeListener(eventName, callback) {
if (this.events[eventName]) {
this.events[eventName] = this.events[eventName].filter(cb => cb != callback)
}
}//filter() 方法创建一个新的数组,新数组中的元素是通过检查指定数组中符合条件的所有元素。
// 只执行一次订阅的事件,然后移除
once(eventName,callback) {
// 绑定的时fn, 执行的时候会触发fn函数
let fn = () => {
callback(); // fn函数中调用原有的callback
this.removeListener(eventName,fn); // 删除fn, 再次执行的时候之后执行一次
}
this.on(eventName,fn)
}
}
使用方式
let em = new EventEmitter();
let workday = 0;
em.on("work", function() {
workday++;
console.log("work everyday");
});
em.once("love", function() {
console.log("just love you");
});
function makeMoney() {
console.log("make one million money");
}
em.on("money",makeMoney);
let time = setInterval(() => {
em.emit("work");
em.removeListener("money",makeMoney);
em.emit("money");
em.emit("love");
if (workday === 5) {
console.log("have a rest")
clearInterval(time);
}
}, 1);
预先加载,是什么如何实现
图片等静态资源在使用前提前请求。
资源后续使用可以直接从缓存中加载,提升用户体验。
加载不是为了减少页面加载时间
预加载只是提前加载除去首轮加载的图片以后要用到的图片,比如通过点击等事件才会用到的图片。
在js中,需要多少预加载图片,就创建多少image对象,再为每个image对象添加图片的src,此时图片也会被提前请求。
var images = new Array();
function preload(){
for(var i = 0;i < preload.arguments.length;i ++){
iamges[i] = new Image();
images[i].src = preload.arguments[i];
}
}
preload(url1,url2,url3);
//也可以将上面的代码改写一下
function preload(){
if(document.images){//document.images:页面上所有图片的集合
var img1 = new Image();
var img2 = new Image();
var img3 = new Image();
img1.src = url1;
img2.src = url2;
img3.src = url3;
}
}
使用ajax
只要是静态资源都可以预加载,包括图片,css,js,可以使用ajax请求这些静态资源,这样也不会影响当前页面。
window.onload = function(){
setTimeout = (function(){
var xhr = new XMLHttpRequest();
xhr.open('GET','js文件地址');
xhr.send('');
xhr = new XMLHttpRequest();
xhr.open('GET','css文件地址');
xhr.send('');
new Image().src = '图片地址';
},1000);
}
手写 call bind apply 内部实现
https://segmentfault.com/a/1190000016960203