今日份的学习(3)

webpack PK rollup

关于webpack

多层次引用和模块化处理需要用webpack解决

  • webpack.config.js

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    module.exports = {
    entry:"./src/index.js",
    output:{
    path:__dirname,
    filename:"./build/bundle.js"
    },
    module:{
    rules:[{
    test:/\.js?$/,
    exclude:/(node_modules)/,
    loader:'babel-loader'
    }]
    }
    }

关于rollup

优点:

  • 打包出来非常的小,能简化输出之后的大小
  • 工具尽量单一,可集成、可扩展
  • 没有冗余代码,webpack有自己封装的东西在打包的文件里
  • 和webpack对比来说,功能比较单一,webpack功能比较强大(学习成本比较高)

  • rollup.config.js

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    import babel from 'rollup-plugin-babel'
    import resolve from 'rollup-plugin-node-resolve'

    export default{
    entry:"src/index.js",
    format:"umd",
    plugins:[
    resolve(),
    babel:({
    exclude:"node_modules/**"
    })
    ],
    dest:"build/bundle.js"
    }

JS模块化的过程

  1. 没有模块的时候(蛮荒时代)

  2. AMD成为标准,require.js(也有CMD)

  3. 前端打包工具出现,可以使用node.js的模块化(CommonJs)

  4. ES6出现,想统一现在的模块化的标准

  5. node.js积极支持,浏览器支持不统一

  6. 你可以自己制造库(lib),但不要自制标准


Class和普通构造函数的区别

  1. class在语法上更贴合面向对象的写法

  2. class在实现继承上更易读、易理解

  3. 本质还是语法糖,使用的是prototype

1
2
3
4
5
6
7
8
9
10
11
class A {
//...
}

const B = new A()

typeof A //'function'

A.prototype.constructor === A //true

B.__proto__ === A.prototype //true

完全符合构造函数原理

而针对继承,构造函数通过

es5:

1
2
3
4
5
6
7
8
9
function A(){
//...
}

function B(){
//...
}

B.prototype = new A() //实现继承

es6:

1
2
3
class B extends A{
//...
} //实现继承

Promise

callback hell写法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
function loadImg(src,callback,fail){
var img = document.createElement("img");
img.onload = function(){
callback(img);
}
img.onerror = function(){
fail();
}

img.src = src;
}

var src = "xxxxxxxxx.png";
loadImg(src,function(img){
console.log(img.width);
},function(){
console.log('failed')
})

Promise写法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
function loadImg(src){
const promise = new Promise(function(resolve,reject){
var img = document.createElement("img");
img.onload = function(){
resolve(img)
}
img.onerror = function(){
reject()
}
img.src = src;
})
return promise;
}

var src = "xxxxxxxxx.png"
var result = loadImg(src);
result.then(function(img){
console.log(img.width)
},function(){
console.log('failed')
});
  • 语法:

    • new Promise实例需要return返回出去

    • new Promise实例传入一个函数,其中有resolve和reject两个参数

    • 成功执行resolve失败执行reject

    • .then监听结果

  • 异常捕获:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    //规定then只传一个参数,异常统一由catch处理
    result.then(function(img){
    console.log(img.width)
    return img;
    }).then(function(img){
    console.log(img.height)
    }).catch(function(ex){
    console.log(ex)
    })
  • Promise.all和Promise.race

    1
    2
    3
    4
    Promise.all([result01,result02]).then(datas => {
    console.log(datas[0])
    console.log(datas[1])
    })
    1
    2
    3
    Promise.race([result01,result02,result03]).then(data => {
    console.log(data)
    })
  • Promise标准之状态变化

    • 三种状态:pending fulfilled rejected
    • 初始状态就是pending
    • pending变为fulfilled或者pending变为rejected
    • 状态变化不可逆’
  • ES7 async await

    • 最直接的同步写法
    1
    2
    3
    4
    5
    6
    7
    const load = async function(){
    const result1 = await loadImg(src) //await后必须跟一个promise实例
    console.log(result1)
    const result2 = await loadImg(src)
    console.log(result2)
    }
    load()
    • 使用了promise且和promise没有冲突
    • 同步函数取代回调函数

原型的实际应用

zepto

  • 原型实现
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
var zepto = {}

zepto.init = function(selector){
var slice = Array.prototype.slice;
var dom = slice.call(document.querySelectorAll(selector));//此时dom是一个数组

return zepto.Z(dom.selector);
}

var $ = function(selector){
return zepto.init(selector);
}

//这个就是一个构造函数
function Z(dom,selector){
var i , len = dom ? dom.length : 0;
for(i = 0; i < len; i++) this[i] = dom[i];
this.length = len;
this.selector = selector;
}

zepto.Z = function(dom,selector){
//通过new 初始化一个实例
return new Z(dom,selector);
}
  • 原型使用
1
2
3
4
5
6
7
8
9
10
11
12
$.fn = {
constructor : zepto.Z,
css : function(key,value){
//...
},
html : function(value){
//...
}
}

//重要
zepto.Z.prototype = Z.prototype = $.fn

jQuery

  • 原型实现
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
var jQuery = function(selector){
//通过new关键字第一步就可以找到构造函数
return new jQuery.fn.init(selector);
}

//定义构造函数
var init = jQuery.fn.init = function(selector){
var slice = Array.prototype.slice;
var dom = slice.call(document.querySelectorAll(selector));

var i,len = dom ? dom.length : 0;
for(i = 0; i < len; i++){
this[i] = dom[i];
}
this.length = len;
this.selector = selector || "";
}
  • 构造函数使用
1
2
3
4
5
6
7
8
9
10
11
12
13
//初始化fn
jQuery.fn = {
constructor : jQuery,
css : function(key,value){
//...
},
html : function(value){
//...
}
}

//定义原型
init.prototype = jQuery.fn;

插件机制

看了jQuery和zepto的原型,有两个很重要的地方就是

1
2
3
4
5
//zepto
zepto.Z.prototype = Z.prototype = $.fn

//jquery
init.prototype = jQuery.fn

为什么不直接咋jQuery或zepto的构造函数的原型上添加这些方法,而是在$.fn或者jQuery.fn上添加方法,再赋值都原型上去呢?

  • 优点:
    1. 只有$会暴露在window全局变量,不会产生污染
    2. 将插件扩展统一到$.fn.xxx这个接口中,方便使用
    3. 满足对扩展开放,对修改封闭的设计原则

什么是单线程,和异步的关系

  • 单线程:只有一个线程,同一时间,只能做一件事情

  • 原因:避免DOM渲染的冲突

    • 浏览器需要渲染DOM
    • JS可以修改DOM结构
      *JS执行的时候,浏览器DOM渲染就会暂停
    • 两段JS也不能同时执行,如果两段JS修改同一个DOM就会有冲突
    • webworker支持多线程,但是不能访问DOM
  • 解决方案:异步

    缺点:

    • 没有按照书写顺序执行,可读性差
    • callback中不易模块化

异步

  • event-loop(事件轮询)
    • JS实现异步的具体解决方案
    • 同步代码直接执行
    • 异步函数先放到异步队列中
    • 待同步函数执行完毕,轮询执行异步队列的函数

jQuery Deffered

1.5版本之前的写法

1
2
3
4
5
6
7
8
9
10
var ajax = $.ajax({
url:'data.json',
success:function(){
console.log('success1')
},
error:function(){
console.log('error')
}
})
console.log(ajax) //返回一个xhr对象

1.5版本之后的写法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
var ajax = $.ajax('data.json')
ajax.done(funcrion(){
console.log('success1')
}).fail(function(){
console.log('error1')
}).done(function(){
console.log('success2')
})

console.log(ajax) //返回一个deffered对象

//或者
var ajax = $.ajax('data.json')
ajax.then(function(){
console.log('success1')
},function(){
console.log('error1')
}).then(function(){
console.log('success2')
},function(){
console.log('error2')
})
//很像promise
  • 关于1.5版本的变化

    • 无法改变js异步和单线程的本质
    • 只能从写法上杜绝callback的形式
    • 它是一种语法糖,但是解耦了代码
    • 很好体现了开放封闭原则(对扩展开放,对修改封闭)
  • 使用jQuery deffered API可以分为两类

    • 第一:主动去操作的 dtd.resolve()dtd.reject()
    • 第二:被动监听 dtd.then()dtd.done()dtd.fail()
    • 这两类应该分开,否则后果很严重
  • Deffered写法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
function waitHandle(){
var dtd = $.Deffered()

var wait = function(dtd){
var task = function(){
console.log('执行完成')
dtd.resolve() //表示异步任务已经完成
//dtd.reject() 表示异步任务失败或出错
}

setTimeout(task,2000)
return dtd //要求返回deffered对象
}

//注意这里一定要有返回值
return wait(dtd)
}

var w = waitHandle()
w.then(function(){
//....
}).then(function(){
//...
})
  • promise的雏形
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
function waitHandle(){
var dtd = $.Deffered()

var wait = function(dtd){
var task = function(){
console.log('执行完成')
dtd.resolve() //表示异步任务已经完成
//dtd.reject() 表示异步任务失败或出错
}

setTimeout(task,2000)
return dtd.promise() //返回的是一个promise的对象而不是一个deffered对象
}

//注意这里一定要有返回值
return wait(dtd)
}

var w = waitHandle()
$.when(w).then(function(){
//....
}).then(function(){
//...
})