Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

前端基础 #21

Open
SunXinFei opened this issue Aug 1, 2019 · 14 comments
Open

前端基础 #21

SunXinFei opened this issue Aug 1, 2019 · 14 comments
Labels

Comments

@SunXinFei
Copy link
Owner

SunXinFei commented Aug 1, 2019

异步与同步

单线程与多线程

js为什么是单线程?js语言在设计之初就是单线程的,原因其实也比较简单,js主要是针对浏览器使用,浏览器中单线程能够保证页面中DOM的渲染不出现异常,也正是这个原因,所以在现在新标准允许JavaScript脚本多线程中有一个严格的要求,子线程不能够进行DOM操作,来严格把控住,所以所谓js多线程,也就只能做一些业务上面的运算,比如数量非常大的数据清洗等等。

任务队列

Philip Roberts的PPT视频《Help, I'm stuck in an event-loop》中已经非常清晰地说明了关于任务队列的讲解:

  1. 所有任务在主线程执行,形成一个执行栈(execution context stack)
  2. 一旦执行栈(execution context stack)中没有了任务,便会去"任务队列"(task queue)中读取任务放置到执行栈(execution context stack)中去执行
  3. 不断重复

宏任务&微任务

image

宏任务一般包括:整体代码script,setTimeout,setInterval, setImmediate。
微任务:Promise,process.nextTick , Object.observe [已废弃], MutationObserver

(()=>{
  console.log('script start') //1

async function async1() {
  await async2()
  console.log('async1 end')//5
}
async function async2() {
  console.log('async2 end') //2
}
async1()

setTimeout(function() {
  console.log('setTimeout')//8
}, 0)

new Promise(resolve => {
  console.log('Promise')//3
  resolve()
})
  .then(function() {
    console.log('promise1')//6
  })
  .then(function() {
    console.log('promise2')//7
  })

console.log('script end')//4
}
)()

https://juejin.im/post/6844903764202094606

浏览器与node的区别

浏览器为:
image
node为:
image
浏览器环境下,microtask 的任务队列是每个 macrotask 执行完之后执行。而在 Node.js 中,microtask 会在事件循环的各个阶段之间执行,也就是一个阶段执行完毕,就会去执行 microtask 队列的任务。

SettimeOut

基于上面的任务队列,settimeOut函数也就可以解释一些参数0之类的问题了,方法先执行主线程中执行栈的方法,settimeout中的方法会放置到任务队列,并且先进先出,执行栈执行完之后,去任务队列中拿出方法放入执行栈去执行,直到结束为止。
其中delay的毫秒参数的含义如下,MDN,里面提到一点很重要实际延迟可能比预期的更长;这一点很容易理解:实际延迟的时间是看该延迟函数方法之前的那些主线程执行栈中方法执行的时间以及队列中放入执行栈去执行的时间。如果两者超出了延迟时间,那么就产生了实际延迟可能比预期的更长的现象,下面是实验代码验证这个观点

delay Optional
The time, in milliseconds (thousandths of a second), the timer should wait before the specified function or code is executed. If this parameter is omitted, a value of 0 is used, meaning execute "immediately", or more accurately, as soon as possible. Note that in either case, the actual delay may be longer than intended; see Reasons for delays longer than specified below.

console.log(1);
setTimeout(function(){console.log(2);}, 0);
setTimeout(function(){console.log(3);}, 0);
console.log(5);
//1
//4
//函数返回undefined
//2 
//3
(()=>{
  console.log(1,Date.now());
  setTimeout(function(){console.log(2,Date.now() );}, 1000);
  setTimeout(function(){console.log(3,Date.now());}, 0);
  console.log(4,Date.now());
})()
//1 1564650928850
//4 1564650928850
//函数返回undefined
//3 1564650929023
//2 1564650929851

这个例子验证了一点,1000毫秒的含义,其实表示的是第一次运行到这里是1564650928851左右的时间,等到队列中的方法执行完之后,此时间隔时间如果超过了1000ms,则立即执行,如果低于1000ms,则去等待1000ms之后去执行。

(()=>{
    console.log(1, Date.now());
    setTimeout(function() {
        console.log(2, Date.now());
    }, 100);
    setTimeout(function() {
        console.log(3, Date.now());
    }, 0);
    console.log(4, Date.now());
}
)()
//1 1564651900863
//4 1564651900863
//函数返回undefined
//3 1564651901027
//2 1564651901027

这里就验证了,所谓的延迟100ms并不一定是相对延迟了100ms之后,就要去执行,实际延迟可能比预期的更长所以当队列执行到此时,时间已经延迟大于了100ms,所以就立即去执行log 2这个方法。

@SunXinFei
Copy link
Owner Author

SunXinFei commented Sep 30, 2019

js中的继承

我们先说下《JS高级程序设计》中提到的组合式继承;继承即为继承父类属性和继承父类方法,组合式继承核心是使用call继承属性;原型对象上挂载要继承的方法;故为代码如下:

//父类构造函数
function SuperType(name){
  this.name = name;
  this.colors = ['red','white']
}
SuperType.prototype.sayName = function(){
  console.log(this.name);
}
function SubType(name,age){
  this.age = age;
  SuperType.call(this, name);
}
SubType.prototype = new SuperType();
SubType.prototype.constructor = SubType;
SubType.prototype.sayAge = function(){
  console.log(this.age);
}

let instance = new SubType('Nical',12);

image

缺点:父类构造函数执行了两次,SubType.prototype = new SuperType();会导致SubType.prototype挂载上父类的属性:name和colors,通过SuperType.call(this, name);在子类实例上挂载属性进行屏蔽。

寄生组合式继承

核心思想:复制一个父类的原型对象赋值给子类的prototype,不必为了指定子类的原型而调用超类构造函数。

/**
        * subType- 子类型构造函数
        * superType-超类型构造函数
        */
        function inheritPrototype(subType, superType) {
            //1.创建了超类(父类)原型的(副本)浅复制
            var prototype = Object.create(superType.prototype);

            /*
                2.修正子类原型的构造函数属性
                 constructor属性也是对象才拥有的,它是从一个对象指向一个函数,含义就是指向该对象的构造函数
                prototype.constructor  未修改前指向的 superType,为了弥补因重写原型而失去的默认constructor属性。
               
            */
            prototype.constructor = subType;
            // 3.将子类的原型替换为超类(父类)原型的(副本)浅复制
            subType.prototype = prototype;
        }
function SuperType(name) {
            this.name = name;
            this.colors = ["red", "blue", "green"];
        }

        SuperType.prototype.sayName = function () {
            alert(this.name);
        };

        function SubType(name, age) {
            //构造函数式继承--子类构造函数中执行父类构造函数
            SuperType.call(this, name);
            this.age = age;
        }
        // 核心:因为是对父类原型的复制,所以不包含父类的构造函数,也就不会调用两次父类的构造函数造成浪费
        inheritPrototype(SubType, SuperType);

        SubType.prototype.sayAge = function () {
            alert(this.age);
        }
        var instance = new SubType("lichonglou");
        console.log(instance.name)
        // console.log(instance.constructor)//指向SubType 如果没有修正原型的构造函数,则会指向父类构造函数

image

@SunXinFei
Copy link
Owner Author

SunXinFei commented Aug 18, 2020

ECMA6设计模式

单例模式

class Singleton{
   constructor(){
     if(!this.constructor.instance){
       this.constructor.instance = this ;
     }
     return this.constructor.instance;
   }
 }

 const instance1 = new Singleton();
 const instance2 = new Singleton();
 console.log(instance1 === instance2);//true

image

发布订阅

class EventEmitter {
        constructor(){
            this._events = {};
        }
        on(event,callback){//事件绑定
            const callbacks = this._events[event] || [];
            callbacks.push(callback);
            this._events[event] = callbacks;
        }
        emit(...args){//事件触发
            const event = [].shift.call(args);//Array.prototype.shift.call(args); //args.shift();
            (this._events[event] || []).forEach(cb=>cb.apply(this,args))
        }
        off(event,callback){//事件解除
            const callbacks = (this._events[event] || []).filter(cb=> cb!==callback);
            this._events[event] = callbacks;
        }
        once(event,callback){//只触发一次,触发后解绑
            const wrap = (...args)=>{
                callback.apply(this,args);
                this.off(event,wrap);
            }
            this.on(event,wrap);
        }
    }
    //on&emit
    eventEmitter.on('abc', test);
    eventEmitter.emit('abc', 3, 666);//3 666
    //off
    eventEmitter.off('abc',test);
    //once
    eventEmitter.once('abc',test);
    eventEmitter.emit('abc',666,1);//666 1
    eventEmitter.emit('abc',888,1);//不输出

@SunXinFei
Copy link
Owner Author

依赖注入(Dependency Injection)控制反转(Inversion of Control)

提升开发效率
提高模块化
便于单元测试

@SunXinFei
Copy link
Owner Author

SunXinFei commented Sep 27, 2020

跨域options

W3C规范:跨域请求中,分为简单请求和复杂请求;所谓的简单请求,需要满足如下几个条件:

  • GET,HEAD,POST请求中的一种;
  • 除了浏览器在请求头上增加的信息(如Connection,User-Agent等),开发者仅可以增加Accept,Accept-Language,Content-Type等;

Accept
Accept-Language
Content-Language
Content-Type (需要注意额外的限制)
DPR
Downlink
Save-Data
Viewport-Width
Width

  • Content-Type的取值必须是以下三个:application/x-www-form-urlencoded,multipart/form-data,text/plain。

简单请求不会触发 CORS 预检请求(嗅探请求)
简单请求之外为复杂请求

@SunXinFei
Copy link
Owner Author

SunXinFei commented Oct 4, 2020

Promise

Promise规范

  • 一个promise的当前状态只能是pending、fulfilled和rejected三种之一。状态改变只能是pending到fulfilled或者pending到rejected。状态改变不可逆。
  • promise的then方法接收两个可选参数,表示该promise状态改变时的回调(promise.then(onFulfilled, onRejected))。then方法返回一个promise,then 方法可以被同一个 promise 调用多次。

静态方法

Promise.resolve()
此方法有一个可选的参数,参数的类型会影响它的返回值,具体可分为三种情况(如下所列),其中有两种情况会创建一个新的已处理的Promise实例,还有一种情况会返回这个参数。

  • 当参数为空或非thenable时,返回一个新的状态为fulfilled的Promise。
  • 当参数为thenable时,返回一个新的Promise,而它的状态由自身的then()方法控制,具体细节已在之前的thenable一节做过说明。
  • 当参数为Promise时,将不做修改,直接返回这个Promise。

then

总的来说:then方法提供一个供自定义的回调函数,若传入非函数,则会忽略当前then方法,回调函数中会把上一个then中返回的值当做参数值供当前then方法调用。
then方法执行完毕后需要返回一个新的值给下一个then调用(没有返回值默认使用undefined)。
每个then只可能使用前一个then的返回值。
再次描述:
对于传入 then 方法的参数,首先判断其是否为 function,判断为否,直接执行 下一个 then 的 success 函数;判断为是,接着判断函数的返回值 res 类型是否为 Promise,如果为否,直接执行下一个 then 的 success 函数,如果为是,通过 then 调用接下来的函数。

实现

//Promise 支持异步
    class SimplePromise {
        constructor(callback) {
            this._value = null;//存储value值
            this.status = 'pending';
            this._onSuccess = [];
            this._onFail = [];
            let resolve = (val)=>{//传递给外部去主动调用的成功函数
                if (this.status === 'pending') {
                    this.status = 'resolve';
                    this._value = val;
                    while (this._onSuccess.length > 0) {//如果队列有方法,则进行调用
                        let temp = this._onSuccess.shift();
                        temp(val);
                    }
                }
            }
            let reject = (val)=>{//传递给外部去主动调用的失败函数
                if (this.status === 'pending') {
                    this.status = 'resolve';
                    this._value = val;
                    while (this._onFail.length > 0) {//如果队列有方法,则进行调用
                        let temp = this._onFail.shift();
                        temp(val);
                    }
                }
            }
            callback(resolve, reject)
        }
        then(onFulfilled, onRejected) {
            switch (this.status) {//状态判断
            case 'resolve'://成功,调用成功回调函数,并传值
                onFulfilled(this._value);
                break;
            case 'reject'://失败,调用失败回调函数,并传值
                onRejected(this._value);
                break;
            case 'pending'://等待状态,则先将函数放入队列中,等待外部调用触发resolve/reject,再进行调用
                this._onSuccess.push(onFulfilled);
                this._onFail.push(onRejected);
                break;
            }
        }
    }
    //测试case
    const pro = new SimplePromise(function(res, rej) {
        setTimeout(function() {
            let random = Math.random() * 10
            if (random > 5) {
                res("success")
            } else {
                rej("fail")
            }
        }, 2000)

    }
    )

    pro.then(function(data) {
        console.log(data)
    }, function(err) {
        console.log(err)
    })
Promise.prototype.all = (promises = [])=>{
    return new Promise((resolve,reject)=>{
        let resultArr = [];
        let count = 0;
        promises.forEach((p,i)=>{
           Promise.resolve(p).then((value)=>{
                count++;
                resultArr[i] = value;
               if(count === promises.length){
                    resolve(resultArr)
               }
           },reject)
        })
    })
}

Promise.prototype.race = (promises = []) => {
    return new Promise((resolve, reject) => {
        if (!Array.isArray(promises)) {
            throw new TypeError(`argument must be a array`);
        }
        for (const p of promises) {
            // 有一个成功就返回成功状态的promise
            // 有一个失败就返回失败状态的promise
            p.then(resolve, reject);
        }
    });
}


let p1 = new Promise(function (resolve, reject) {
    setTimeout(function () {
        resolve(1)
    }, 1000)
})
let p2 = new Promise(function (resolve, reject) {
    setTimeout(function () {
        resolve(2)
    }, 2000)
})
let p3 = new Promise(function (resolve, reject) {
    setTimeout(function () {
        resolve(3)
    }, 3000)
})
Promise.all([p3, p1, p2]).then(res => {
    console.log(res) // [3, 1, 2]
});
Promise.race([p3, p1, p2]).then(res => {
    console.log(res) // 1
});

实现一个异步队列,可以设置并发请求数量,应用场景例如:分片上传可以控制三个任务

class PromiseQueue{
	constructor(limit=1){
		this._list = [];
		this._current= 0;
		this._limit = limit;
	}
	add(promiseFn){
		this._list.push(promiseFn);
		this.loadNext();
	}
	loadNext(){
		if(this._list.length === 0 || this._limit === this._current) return;
		this._current++;
		const fn = this._list.shift();
		const promise = fn();
		promise.then(this.onLoaded.bind(this)).catch(this.onLoaded.bind(this));
	}
    onLoaded(){
    	this._current--;
    	this.loadNext();
    }
}

const q = new PromiseQueue(2);
[1, 2, 3, 4, 5].forEach(v => {
    q.add(() => new Promise((resolve) => {
       setTimeout(() => {
           console.log(v);
           resolve();
       }, 1000);
    }));
});

@SunXinFei
Copy link
Owner Author

SunXinFei commented Oct 4, 2020

image

防抖&截流

function debounce(func, delay){
        let id;
        return function (...args){
           clearTimeout(id);
           id = setTimeOut(()=>{
               func.apply(this,args)
           },delay)

        }
    }

防抖(debounce) :在事件触发后的n秒之后,再去执行真正需要执行的函数,如果在这n秒之内事件又被触发,则重新开始计时。常见的操作就是搜索,中间不断的输入,我们都可以忽略,只获取最后停止输入的时候,才去请求后端。

//截流
/**
 * 连续的方法 延迟,如持续输入数据,搜索
 * @param {Function} fn 需要延迟运行的的回调函数
 **/
let previous = 0
export const throttle = (fn,delay) => {
    return function() {
        let now = +new Date();
        if (now - previous > delay) {
          fn.apply(this, arguments);
          previous = now;
        }
    }
}
//调用地方
    /**
     * 处理滚动视图的滚动事件
     */
    scrollFn(e) {
      throttle(this.aaa, 1000)(666);
    },
   aaa(num){
      console.log(this.test,num);
    },

节流(throttling):规定好一个单位时间,触发函数一次。如果在这个单位时间内触发多次函数的话,只有一次是可被执行的。想执行多次的话,只能等到下一个周期里。常见的操作比如滚动事件,每隔n毫秒,我们去请求,或者拖拽,每隔n毫秒改变dom的位置。还比如resize窗口。

@SunXinFei
Copy link
Owner Author

image

@SunXinFei
Copy link
Owner Author

异常捕获

  • try……catch
  • window.onerror
  • window.addEventListener('error')
  • window.addEventListener('unhandledrejection') //promise错误

资源加载的错误捕获

imgObj.onerror()
performance.getEntries(),获取到成功加载的资源,对比可以间接的捕获错误
window.addEventListener('error', fn, true), 会捕获但是不冒泡,所以window.onerror 不会触发,捕获阶段可以触发

Vue、React中

Vue有 errorHandler,React有 componentDidCatch 进行错误捕获

@SunXinFei
Copy link
Owner Author

SunXinFei commented Oct 12, 2020

网络安全

XSS跨站脚本攻击

方式有很多种,大部分都是进行脚本注入,举个例子:在url里面做操作,将普通访问的url修改为url+script脚本,scrript脚本里面可以做很多功能比如:获取cookie并发送到黑客的服务器(用cookie的httponly解),去提交一个数据...等等。
前端的框架都有成熟的xss跨站脚本防御,一般是用户侧提交的东西,都会进行编码,来防止注入。

CSRF跨站请求伪造

这个一般就是首先在A这个安全网站登陆,用户打开了B这个危险网站,B网站里面加了一个img标签,src是A网站的一个get请求,这时候浏览器会将A网站的请求发出去,并且自动携带了A的cookie,A网站后台没有任何保护的情况下就以为是用户在操作,从而被攻击。
使用token解决就是利用了同源策略里面B网站是拿不到A网站任何dom元素或者js脚本执行结果的,将A网站上html加入后端生成的token,发送请求的时候携带上(header中或者链接中都可以),后端拿着token去校验有效性。这样危险网站B就因为不容易伪造token,而伪装失败。
或者设置:
Set-Cookie: CookieName=CookieValue; SameSite=Strict;
Strict最为严格,完全禁止第三方 Cookie,跨站点时,任何情况下都不会发送 Cookie。换言之,只有当前网页的 URL 与请求目标一致,才会带上 Cookie。
Set-Cookie: CookieName=CookieValue; SameSite=Lax;
Lax规则稍稍放宽,大多数情况也是不发送第三方 Cookie,但是导航到目标网址的 Get 请求除外。
Set-Cookie: widget_session=abc123; SameSite=None; Secure
Chrome 计划将Lax变为默认设置。这时,网站可以选择显式关闭SameSite属性,将其设为None。不过,前提是必须同时设置Secure属性(Cookie 只能通过 HTTPS 协议发送),否则无效。
http://www.ruanyifeng.com/blog/2019/09/cookie-samesite.html
https://developer.mozilla.org/zh-CN/docs/Web/HTTP/Headers/Set-Cookie

点击劫持

最常见的是恶意网站使用 <iframe> 标签把我方的一些含有重要信息类如交易的网页嵌入进去,然后把 iframe 设置透明,用定位的手段的把一些引诱用户在恶意网页上点击。这样用户不知不觉中就进行了某些不安全的操作。
使用 HTTP 头防范: 通过配置 nginx 发送 X-Frame-Options 响应头,这样浏览器就会阻止嵌入网页的渲染。更详细的可以查阅 MDN 上关于 X-Frame-Options 响应头的内容。 add_header X-Frame-Options SAMEORIGIN;
使用 JS 防范: if (top.location.hostname !== self.location.hostname) { alert("您正在访问不安全的页面,即将跳转到安全页面!"); top.location.href = self.location.href; }

@SunXinFei
Copy link
Owner Author

SunXinFei commented Oct 13, 2020

for

for in会输出自身以及原型链上可枚举的属性。
Object.keys是es5中新增的方法,用来获取对象自身可枚举的属性键。
Object.getOwnPropertyNames也是es5中新增的方法,用来获取对象自身的全部属性名。
Object.keys的效果和for in+hasOwnProperty一样

for of

原生具备 Iterator 接口的数据结构如下。

Array
Map
Set
String
TypedArray
函数的 arguments 对象
NodeList 对象
https://juejin.im/post/6844904129471463432
https://segmentfault.com/q/1010000011767450?sort=created
https://stackoverflow.com/questions/37576685/using-async-await-with-a-foreach-loop
https://lavrton.com/javascript-loops-how-to-handle-async-await-6252dd3c795/

类型判断

typeof

运算数为数字 typeof(x) = "number"
字符串 typeof(x) = "string"
布尔值 typeof(x) = "boolean"
对象,数组和null typeof(x) = "object"
函数 typeof(x) = "function"
未定义 typeof(undefined) = 'undefined'
所以判断不出对象和数组以及null

instanceof

a instanceof A 原理就是a沿着__proto__去查找原型对象,A则沿着prototype去查找,如果两者可以找到相同的则返回true

Object.prototype.toString.call()一劳永逸

@SunXinFei
Copy link
Owner Author

SunXinFei commented Oct 15, 2020

call实现

//fun.call(thisArg, arg1, arg2, ...)
Function.prototype.call  = Function.prototype.call || function(context){
	var context = context || window;
	context.fn = this; // 获取调用call的函数
	var args = [];
	for(var i=1, len=arguments.length ; i<len ; i++){
		args.push(arguments[i]);
	}
	var result = eval('context.fn(' + args +')');
	delete context.fn;
	return result;
}

apply

//fun.apply(thisArg,[args]);
Function.prototype.apply = Function.prototype.apply || function(context){
	var context = context || window;
	context.fn = this;
	// var args = [];
	// for(var i=1, len=arguments.length ; i<len ; i++){
	// 	args.push(arguments[i]);
	// }
	var args = arguments[1];
	var result = eval('context.fn('+args+')');
	delete context.fn;
	return result;
}

bind

//fun.bind(thisArg[, arg1[, arg2[, ...]]])
//基础版本
Function.prototype.bind = Function.prototype.bind || function(context){
	var self = this;
	var context = context || window;
	var bindArgs = Array.prototype.slice.call(arguments,1);
	return function(){
		var tmpArgs = Array.prototype.slice.call(arguments);
		var newArgs = bindArgs.concat(tmpArgs);
		return self.apply(context, newArgs);
	}
}

@SunXinFei
Copy link
Owner Author

SunXinFei commented Oct 19, 2020

http2.0

http1.1的痛点

  1. 一个浏览器对一个域名有TCP链接限制大概在6个左右,达到这个上限之后会进行排队,所以在1.1阶段,很多电商或门户网站都是使用域名分片,将页面的图片资源,挂载到不同的域名,如(img1.360buy.com、img2.360buy.com),来饶开这个限制。但是我们知道每个 TCP 连接本身需要经过 DNS 查询、三步握手、慢启动等。
  2. 虽然http1.1有默认开启keep-alive,来减少tcp连接数量,但是仍然不能解决线头阻塞 (Head Of Line Blocking) 的问题,即为每个 TCP 连接同时只能处理一个请求 - 响应,浏览器按 FIFO 原则处理请求,如果上一个响应没返回,后续请求 - 响应都会受阻。 HTTP 1.x 只能严格串行地返回响应。特别是,HTTP 1.x 不允许一个连接上的多个响应数据交错到达(多路复用),因为HTTP1.x需要每条请求都是可是识别,按顺序发送,否则server就无法判断该相应哪个具体的请求,因而一个响应必须完全返回后,下一个响应才会开始传输。
  3. 为了尽可能减少请求数,需要做合并文件、雪碧图、资源内联等优化工作,但是这无疑造成了单个请求内容变大延迟变高的问题,且内嵌的资源不能有效地使用缓存机制。

http2.0

  • 多路复用是指,在同一个域名下,开启一个TCP的connection,每个请求以stream的方式传输,每个stream有唯一标识,connection一旦建立,后续的请求都可以复用这个connection并且可以同时发送,server端可以根据stream的唯一标识来相应对应的请求。
  • HTTP2的头部会减小,从而减少流量传输

在http2.0不再适用的优化

1、域名分片
HTTP/2 对于同一域名使用一个 TCP 连接足矣,过多 TCP 连接浪费资源而且效果不见得一定好
而且资源分域会破坏 HTTP/2 的优先级特性,还会降低头部压缩效果
2、资源合并
资源合并会不利于缓存机制,而且单文件过大对于 HTTP/2 的传输不好,尽量做到细粒化更有利于 HTTP/2 传输

http2.0适用的优化

使用 HTTP/2 尽可能用最少的连接,因为同一个连接上产生的请求和响应越多,动态字典积累得越全,头部压缩效果也就越好,而且多路复用效率高,不会像多连接那样造成资源浪费
为此需要注意以下两点:

  • 同一域名下的资源使用同一个连接,这是 HTTP/2 的特性
  • 不同域名下的资源,如果满足能解析到同一 IP 或者使用的是同一个证书(比如泛域名证书),HTTP/2 可以合并多个连接

参考:
https://juejin.im/post/6844903667569541133
https://segmentfault.com/n/1330000009409949
https://www.zhihu.com/question/34401250
补充:TCP连接的三次握手
第一次握手:主机A发送位码为syn=1,随机产生seq number=1234567的数据包到服务器,主机B由SYN=1知道,A要求建立联机;
第二次握手:主机B收到请求后要确认联机信息,向A发送ack number=(主机A的seq+1),syn=1,ack=1,随机产生seq=7654321的包;
第三次握手:主机A收到后检查ack number是否正确,即第一次发送的seq number+1,以及位码ack是否为1,若正确,主机A会再发送ack number=(主机B的seq+1),ack=1,主机B收到后确认seq值与ack=1则连接建立成功。
image

@SunXinFei
Copy link
Owner Author

函数柯里化

function add(x){
    let sum = x;
    function fn (y){
        sum+=y
        return fn;
    }
    fn.valueOf=()=>{
      return sum;
    }
    fn.toString=()=>{
        return sum;
    };
    return fn;
  }

  console.log(add(2)(3));
  console.log(add(1)(2)(3)(4))

https://segmentfault.com/q/1010000004342477

@SunXinFei
Copy link
Owner Author

前端模块化

外部模块的管理

每引用一个外部模块,我们都要去下载对应的js文件引入项目,
弊端:

  • 使用上缺乏便利性
  • 难以跟踪各个外部模块的来源
  • 没有统一的版本管理机制
    NPM时代以后外部模块的使用方式

内部模块的管理

立即调用的函数表达式”(IIFE)去组织模块

  • 将每个模块包裹在一个函数作用域里面执行,这样就可以最大程度地避免污染全局执行环境
  • 通过执行匿名函数得到模块输出,可以暴露给下面的其他模块使用
    AMD规范(Asynchronous Module Definition)异步模块定义,实现为requireJS
    CMD即Common Module Definition,意为“通用模块定义”。实现为sea.js
    CMD && AMD的区别

从上面的代码比较中我们可以得出AMD规范和CMD规范的区别

一方面,在依赖的处理上

AMD推崇依赖前置,即通过依赖数组的方式提前声明当前模块的依赖
CMD推崇依赖就近,在编程需要用到的时候通过调用require方法动态引入
另一方面,在本模块的对外输出上

AMD推崇通过返回值的方式对外输出
CMD推崇通过给module.exports赋值的方式对外输出

https://zhuanlan.zhihu.com/p/265632724?utm_source=tuicool&utm_medium=referral

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

1 participant