解析:
setState
處於equeueSetState()
中,首先將particalState
放入_pendingStateQueue
中,調用enquequeUpdate()
判斷是否爲isBratchingUpdates
批量更新策略,若是是批量更新策略,則調用dirtyComponent()
將組件放入dirtyComponent
,若是不是批量更新策略,則直接調用bratchUpdate
即react中的默認批量更新策略,此時會進入ReactDefaultBatchStrategy.batchedUpdates
,(某個初始isBratchingUpdates
爲false
),調用事務流(含wrapper,inital,close,調用過程爲inital,perform,close),transaction.perform(enQueueUpdate執行函數),此時將修改isBatchingUpdates
爲true,再次回調時將進入dirtyComponent
,事務流結束時將修改isBatchingUpates: false
,同時執行flushBatchedUpdates
將遍歷dirtyComponent
內的組件,根據調用的前後順序執行updateComponent
,進而更新props
、state
。updateComponent
中有一代碼Object.assign(nextState, typeof partical === 'function' ? partical.call(inst, nextState, props, context): partical)
此代碼可解決屢次執行this.setState修改某個數值時值不如預期的狀況。不如預期的緣由: this.setState()
已經在bratchedUpdates
更新事務流中,故此時isBatchingUpdates
爲true
將會進入dirtyComponent
,但若是爲timeout(callback,timeInt)
將會不在batchedUpdates
的事務流中,此時isBatchingUpdates
爲false
將會進入transaction
的事務流而後執行dirtyComponent
,最後執行事務流流中的close
函數,達到更新界面的效果(isBatchingUpdates: false, flushBratchUpdate
); 結論: this.setState將會進入一個待更新的隊列,並不保證同步更新,僅經過回調函數可拿到setState修改後的值,setState一般會集齊一些組件狀態後更新組件,保證性能,代碼內涵即react的批量更新策略。React.createElement()
含type(標籤元素),attributes(屬性),children(標籤子節點);會在render函數被調用的時候執行,render調用返回一個element。格式以下{
type: 'div',
props: {
className: 'cn',
children: [
{
type: function Header,
props: {
children: 'Hello, This is React'
}
},
{
type: 'div',
props: {
children: 'start to learn right now!'
}
},
'Right Reserve'
]
}
}
複製代碼
updateComponent
涉及三個生命週期函數(更新渲染):shouldComponentUpdate componentWillUpdate componentDidUpdate
1.計算nextState => shouldComponentUpdate 2.render獲得nextElement元素 => componentWillUpdate 3.preElement於nextElement進行diff算法更新界面 => componentdidUpdatepromise
,ajax回調中resolve()
獲得異步請求後的數據,Promise.all([])發異步請求時經過async和await同步的方式寫異步代碼,同時保證了多個異步請求執行結束後渲染界面。promise
直接發出兩個promise請求,await等待兩個promise的值都存在開始下一步。 或者generator函數等待兩個promise值,在第一個next()函數拿到promise值在then函數內調用next()拿到下一個異步請求的值,一樣實現併發操做拿到請求值。1. media查詢,同上
2. rem適配,相對於根元素的大小,在理想視口的狀況下(width = device-width)即css像素隨着不一樣的佈局視口做出等比改變,可是css像素和物理像素不變。優勢:利用了理想視口 缺點:須要計算,出現小數點,字符顯示不完整
<script>
let ele = document.createElement('style');
let size = document.documentElement.clientWidth * 16 / 375; //佈局視口
ele.innerHTML = 'html{font-size:'+ size +' px !important}';
document.head.appendChild(ele);
</script>
<script>
//rem適配
let styleElem = document.createElement('style');
let size = (document.documentElement.clientWidth * 16 * dpr) / 375;
styleElem.innerHTML = 'html{font-size:' + size + 'px!important}';
document.appendChild(styleElem);
let metaElem = document.querySelector('meta[name=viewport]');
let dpr = window.devicePixelRatio || 1; //像素比
metaElem.conent =
'initial-scale=' + 1 / dpr + ',maximun=' + 1 / dpr + ',minimun=' + 1 / dpr + 'user-scalable=no';
</script>
3. viewport 視口: 佈局視口(document.documentElement.clientWidth) 可視視口(document.innerWidth) 理想視口(screen.width)
經過1/dpr實現等比縮放,改變了佈局視口的大小,實現1px css像素對應1px 物理像素,同時保證rem的css知足佈局,font-size須要*dpr(縮小布局視口增大,css像素應實現等比改變)
<script>
//rem適配
let styleElem = document.createElement('style');
let size = (document.documentElement.clientWidth * 16 * dpr) / 375;
styleElem.innerHTML = 'html{font-size:' + size + 'px!important}';
document.appendChild(styleElem);
let metaElem = document.querySelector('meta[name=viewport]');
let dpr = window.devicePixelRatio || 1; //像素比
metaElem.conent =
'initial-scale=' + 1 / dpr + ',maximun=' + 1 / dpr + ',minimun=' + 1 / dpr + 'user-scalable=no';
</script>
/* 媒體查詢 */
@media only screen and (device-pixel-ratio: 2) {
#test {
font-size: 12 * 2 + 'px';
transform: scaleY(0.5);
}
}
4. 不一樣設備下佈局視口一致,css大小不變,但css像素對應物理設備像素改變
<script>
let metaElem = document.createElement('meta');
let scale = documnet.createElement.clientWidth / 750;
metaElem.name = 'viewport';
metaElme.content = `initial-scale=${scale}, maximun=${scale}, minimun=${scale}, user-scalable=no`;
document.head.appendChild(metaElem);
</script>
複製代碼
<style>
/* transition解決display問題 */
div>ul{
visibility: hidden;
/* display: none; */
/* height: 0px; */
opacity: 0;
overflow: hidden;
transition: visibility 0s, opacity 0.5s linear;
}
div:hover>ul{
visibility: visible;
/* display: block; */
opacity: 1;
/* height: auto; */
}
</style>
</head>
<body>
<div>
<ul>
<li>fd</li>
<li>fd</li>
<li>fd</li>
<li>fd</li>
</ul>
</div>
</body>
複製代碼
下降請求量:合併資源,減小 HTTP 請求數,minify / gzip 壓縮,webP,lazyLoad。javascript
加快請求速度:預解析DNS,減小域名數,並行加載,CDN 分發。css
緩存:HTTP 協議緩存請求,離線緩存 manifest,離線數據緩存localStorage。html
渲染:JS/CSS優化,加載順序,服務端渲染,pipeline。前端
前端後端渲染的區別 答案相似ajax的優缺點 前端渲染:優勢:1. 操做js實現界面數據改變,局部刷新界面,無需每次完整加載頁面java
懶加載,只需加載可視區的數據node
利用js實現一些酷炫效果,一些操做能夠在前端作,減輕服務器的壓力,整個網絡的數據量小react
實現了先後端分離,經過後端提供的接口獲取數據,渲染界面 缺點:1.沒法後退獲取上一次的狀態 2.需加載過多的js css資源致使首頁性能差3.暴露更多的數據交互邏輯,存在安全問題 後端渲染:1. 首頁性能好,直接顯示一個完整的頁面,發回一整個html頁面 2.勿需加載css js資源jquery
//經典的三次反轉方法:實現數組循環右移k位
void Reverse(int* array, int p, int q)
{
for (; p<q; p++, q--)
{
int temp = array[q];
array[q] = array[p];
array[p] = temp;
}
}
void RightShift(int* array,int n, int k)
{
k %= n;
Reverse(array, 0, n - k - 1);
Reverse(array, n - k, n - 1);
Reverse(array, 0, n - 1);
}
複製代碼
var sumNum = (arr,sum) => {
for(var i = 0; i < arr.length; i++){
let target = sum - arr[i];
if(arr.indexOf(target) !== -1){
if(i !== arr.indexOf(target)){
return [i,arr.indexOf(target)]
}
}
}
return [];
//map解法
var map = new Map();
for(var i = 0; i < arr.length; i++){
let target = sum - arr[i];
if(map.get(target) && map.get(target) != i){
return [i, map.get(target) ]
}else{
map.set(arr[i], i);
}
}
}
複製代碼
import React from 'react'
import PropTypes from 'prop-types'
class context extends React.component{
//聲明context對象屬性
static childContextProps = {
backgroundcolor: PropTypes.string,
color: PropTypes.func
}
//返回context對象
getChildContext(){
return{
backgroundcolor: 'red',
color: 'blue'
}
}
render(){
}
}
//有狀態組件使用context
class StateComponent extends React.Component{
static contextTypes = {
backgroundcolor: 'red'
}
render(){
let {backgoundcolor, color} = this.context; //僅能訪問到backgroundcolor屬性
}
}
//無狀態組件使用context
function NoState(props, context){
return context.color
}
NoState.contextProps = {
color: PropTypes.func
}
複製代碼
let ContextComponent = React.createContext({
backgroundcolor: 'red',
color: 'blue'
});
//生產者
<ContextComponent.Provider value={backgroundcolor: 'red', color: 'blue'}>
</ContextComponent.Provider>
//消費者 經過Consumer 內部爲一函數得到context對象值
<ContextComponent.Consumer>
(context) => {
console.log(context.color);
}
</ContextComponent.Consumer>
複製代碼
console.log('1');
setTimeout(function() {
console.log('2');
process.nextTick(function() {
console.log('3');
})
new Promise(function(resolve) {
console.log('4');
resolve();
}).then(function() {
console.log('5')
})
})
process.nextTick(function() {
console.log('6');
})
new Promise(function(resolve) {
console.log('7');
resolve();
}).then(function() {
console.log('8')
})
setTimeout(function() {
console.log('9');
process.nextTick(function() {
console.log('10');
})
new Promise(function(resolve) {
console.log('11');
resolve();
}).then(function() {
console.log('12')
})
})
打印順序:1 7 6 8 2 4 3 5 9 11 10 12
注意事件循環一個事件執行完成才執行下一個任務
複製代碼
setTimeout(()=>{
console.log('timer1')
Promise.resolve().then(function() {
console.log('promise1')
})
}, 0)
setTimeout(()=>{
console.log('timer2')
Promise.resolve().then(function() {
console.log('promise2')
})
}, 0)
瀏覽器:timer1 promise1 timer2 promise2
Node:timer1 timer2 promise1 promise2
複製代碼
const fs = require('fs')
const starttime = Date.now()
let endtime
fs.readFile('text.txt', () => {
endtime = Date.now()
console.log('finish reading time: ', endtime - starttime)
})
let index = 0
function handler () {
if (index++ >= 1000) return
console.log(`nextTick ${index}`)
process.nextTick(handler)
// console.log(`setImmediate ${index}`)
// setImmediate(handler)
}
handler()
複製代碼
<ul>
<li></li>
<li></li>
<li></li>
</ul>
<!--事件委託-->
var ulist = document.getElementsByTagName('ul');
ullist.onclick = function(e){
if(e.target.nodeName.toLowerCase() === 'li'){
console.log('li click!');
}
}
複製代碼
var util = {};
//獲取 Ajax 請求以後的 json
util.json = function (options) {
var opt = {
url: '',
type: 'get',
data: {},
success: function () {
},
error: function () {
},
};
Object.assign(opt, options);
//IE兼容性處理:瀏覽器特徵檢查。檢查該瀏覽器是否存在XMLHttpRequest這個api,沒有的話,就用IE的api
var xhr = XMLHttpRequest ? new XMLHttpRequest() : new window.ActiveXObject('Microsoft.XMLHTTP');
var data = opt.data,
var type = opt.type.toUpperCase();
var dataArr = [];
if (opt.url) {
var url = opt.url;
}
for (var key in data) {
dataArr.push(key + '=' + data[key]);
}
if (type === 'GET') {
url = url + '?' + dataArr.join('&');
xhr.open(type, url.replace(/\?$/g, ''), true);
xhr.send();
}
if (type === 'POST') {
xhr.open(type, url, true);
// 若是想要使用post提交數據,須要明確設置Request Header
xhr.setRequestHeader("Content-type", "application/x-www-form-urlencoded");
xhr.send(dataArr.join('&'));
}
xhr.onreadystatechange = function () {
if (xhr.status === 200 || xhr.status === 304) {
//304表示:用緩存便可。206表示獲取媒體資源的前面一部分
var res;
if (opt.success && opt.success instanceof Function) {
res = xhr.responseText;
if (typeof res === 'string') {
//將字符串轉成json
res = JSON.parse(res);
opt.success.call(xhr, res);
}
}
} else {
if (opt.error && opt.error instanceof Function) {
opt.error.call(xhr, res);
}
}
};
}
注意兼容性,XMLHttpRequest兼容性,window.XMLHttpReques,不然 new ActiveXObject("Microsoft.XMLHTTP");解決異步請求對象兼容
post需設置請求頭 xhr.setRequestHeader('Content-Type','application/x-www-form-urlencoded')
get請求url用?鏈接參數
複製代碼
優勢:不刷新頁面則更新數據;異步於服務器進行通訊(不打斷用戶操做,更迅速的相應能力);前端和後端負載均衡(將服務端的任務在客戶端處理,減輕服務器的帶寬負擔,節約空間和帶寬租用成本);數據和界面分離,先後端分離,提升開發效率。 缺點:破壞了back和History按鈕,沒法回到前一個頁面狀態,暴露了更多數據引起安全問題如(SQL注入攻擊和基於Credential安全漏洞);違背URL和資源定位的褚總,同一URL可獲得不一樣的內容 使用場景:基本的數據操做和數據過濾 不適用場景:大量的文本替換 導航和搜索ios
// B中的僞代碼
window.onhashchange = function () { //經過onhashchange方法監聽,url中的 hash 是否發生變化
var data = window.location.hash;
};
複製代碼
// 在窗口B中監聽 message 事件
Awindow.addEventListener('message', function (event) { //這裏強調的是A窗口裏的window對象
console.log(event.origin); //獲取 :url。這裏指:http://A.com
console.log(event.source); //獲取:A window對象
console.log(event.data); //獲取傳過來的數據
}, false);
複製代碼
//將對象轉化爲&鏈接後的字符串
function dataToUrl(data){
let res = [];
for(let key in data){
res.push(encodeURIComponent(key) + '=' + encodeURIComponent(data[key]));
}
return res.join('&');
}
function jsonp(url, data, success, errorCb, time){
//callback參數
let cb = 'cb' + Math.floor(Math.random() * 10);
//設置callback = cb
data.callback = cb;
let flag = url.indexOf('?') > -1 ? '&' : '?';
let script = document.createElement('script');
script.setAttribute('src') = url + flag + dataToUrl(data);
window[cb] = function(data){
clearTimeout(script.timer);
window[cb] = null;
document.head.removeChild(script);
success && success(data);
}
if(time){
script.timer = setTimeOut(function(){
clearTimeout(script.timer);
window[cb] = null;
document.head.removeChild(script);
errorCb && errorCb('超時訪問');
},time);
}
document.head.appendChild(script);
}
//後端實現
onst http = require('http');
const { parse } = require('url');
// 假設這是在數據庫中找到的數據~
const data = {
name: 'russ',
age: 20,
gender: 'male'
};
const server = http.createServer((req, res) => {
const url = parse(req.url, true); // 解析 url
// 只有路由爲 `/user` 的 GET 請求會被響應
if (req.method === 'GET' && url.pathname === '/user') {
const { callback } = url.query; // 獲取 callback 回調名稱
if (callback) // 若是傳遞了 callback 參數,說明是 JSONP 請求
return res.end(`${callback}(${JSON.stringify(data)})`);
else // 沒傳遞 callback,直接當作不跨域的 GET 請求返回數據
return res.end(JSON.stringify(data));
}
return res.end('Not Found'); // 不匹配的路由返回錯誤
});
server.listen(3000, () => {
console.log('Server listening at port 3000...');
});
複製代碼
/**
* 1.考慮兼容性 IE
* 2.考慮請求方式
* 3.掌握原生ajax的實現過程
*/
function ajax(optionsArg) {
let options, xhr;
options = {
type: 'get',
url: '',
data: {},
contentType: '',
success: function() {},
error: function() {}
};
Object.assign(options, optionsArg);
//兼容IE
xhr = window.XMLHttpRequest ? new XMLHttpRequest() : new ActiveXObject(Microsoft.XMLHttp);
let { url, type, data, success, error } = options;
let dataArr = objectToUrl(data).join('&');
if (type.toLowerCase() === 'get') {
url = url.indexOf('?') === -1 ? url + '?' + dataArr : url + dataArr;
xhr.open(type, url);
xhr.send();
}
if (type.toLowerCase() === 'post') {
xhr.open(type, url); //ture爲異步 false爲同步
xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
xhr.send(dataArr);
}
xhr.onreadystatechange = function() {
if (xhr.readystate === 4 && ((xhr.status >= 200 && xhr.status < 300) || xhr.status === 304)) {
//請求相應成功
let res = JSON.parse(xhr.responseText); //轉化爲對象
if (successs && typeof success == 'function') {
success.call(undefined, res);
}
} else {
if (error && typeof error === 'function') {
error.call(undefined, xhr);
}
}
};
}
//將對象轉化成等號相連的數據
function objectToUrl(data) {
let res = [];
for (let key in data) {
res.push(key + '=' + data[key]);
}
return res;
}
複製代碼
/**
* async await原理 特色:
* 1. 同步的方式寫異步代碼,async非阻塞異步方式執行, 遇到await函數內部等待, async不阻塞
* 2. 將自動動執行器和generator封裝在一個函數
* 3. async 返回一個promise對象 await等待一個promise對象或者當即值等數據,且成對存在
*/
//自動執行器
function run(gen) {
return new Promise(function(resolve, reject) {
//生成一個iterator迭代器
let it = gen();
//next自動執行
function next(fn) {
let next;
try {
next = fn(); //獲得yield的結果
} catch (err) {
reject(err);
}
if (next.done) {
resolve(next.value); // 執行完畢將返回值做爲resolve決議值
}
Promise.resolve(next.value).then(
function(val) {
next(function() {
it.next(val);
}); // 決議後繼續執行
},
function(err) {
next(function() {
it.throw(err);
}); //拋出錯誤執行的時候進行捕獲
}
);
}
next(function() {
return it.next(undefined);
}); //首次執行無參數傳遞
});
}
/**
* thunk函數:
* 1. 將回調的執行結果給回調函數,並返回回調函數的函數
* 2. 將回調函數的全部權給了thunk函數
* 3. 對函數的一次封裝
*/
//例子
function thunk(fileName){
return function(callback){
return fs.readFile(fileName, callback); //執行結果給回調函數,返回這個回調函數的函數,回調全部權賦予thunk函數
}
}
//yield等待的則是thunk函數和promise對象 方便自動自動執行 thunk函數的自動執行器
function run(gen){
let it = gen();
function next(val){
let next = it.next(val);
if(next.done){
return next.value;
}
next.value(next); //回調函數獲取值
}
next(undefined); //傳遞數據
}
//promise的自動執行 這些都是簡化版 具體自動執行依據 async 函數 自動執行器+generator
function run(gen) {
let it = gen();
function next(val) {
let res = it.next(val);
(function handle(res) {
if (res.done) {
return res.value;
}
Promise.resolve(res.value).then(
next,
function(err) {
handle(it.throw(err)); //經過閉包的方式處理錯誤狀況
}
);
})(res);
}
next(undefined);
}
複製代碼
//父類
function Person(name, age, places) {
this.name = name;
this.age = age;
this.places = places;
}
//原型方法 this指向的是實例對象 this = Parent.prototype 隱式綁定
Person.prototype.getName = function() {
return this.name;
};
//靜態方法
Person.say = function() {
return 'I am the super class!';
};
//子類
function Student(school, id) {
this.school = school;
this.id = id;
}
Student.prototype.getSchool = function() {
return this.school;
};
//1. 原型鏈式繼承 全部實例共享原型上的引用屬性, 沒法傳遞參數
Student.prototype = new Person('wq', '45', ['cd', 'zy']);
let stu1 = new Student('sic', '12');
let stu2 = new Student('sic', '32');
stu1.places.push('none');
//全部實例共享引用類型的原型屬性
console.log(stu1.places, stu2.places);
console.log(stu1.name, stu2.name);
//2. 構造函數式繼承 能夠傳遞參數,每次建立實例都需執行方法,未實現原型繼承
function Jober(name, age) {
Person.call(this, name, age);
}
let job1 = new Jober('jober', '22');
console.log(job1.name, job1.age);
//3. 組合式繼承 子類原型上有多餘的父類的屬性,父類函數執行了2次
function Farmer(name, age) {
Person.call(this, name, age);
}
//實現原型方法繼承
Farmer.prototype = new Person();
let farmer1 = new Farmer('farmer', '89');
console.log(farmer1.name, farmer1.age, farmer1.getName());
//4. 寄生組合式繼承 Object.create + 構造式繼承/ sub.prototype.__proto__ = Super.prototype / Object.setPrototypeOf(sub.prototype, Super) / Object.setPrototypeOf(Sub, Super);
//Object.create原生實現
function objCreate(obj) {
function F() {}
F.prototype = obj;
return new F(); //返回一個對象o, o.__proto__ = obj;
}
function Boss(name, age) {
Person.call(this, name, age);
}
// Boss.prototype = Object.create(Person.prototype, {'constructor' : {'value': 'Sub'}});
// Boss.prototype.__proto__ = Person.prototype;
Object.setPrototypeOf(Boss.prototype, Person.prototype);
Object.setPrototypeOf(Boss, Person); //靜態方法實現原型繼承
let boss1 = new Boss('boss', '56');
console.log(boss1.name, boss1.age, boss1.getName());
console.log(Boss.say());
複製代碼
var Foo = function() {
this.foo = 21;
};
{
const foo = new Foo(); // ReferenceError: Foo is not defined
class Foo {
constructor() {
this.foo = 37;
}
}
}
function a(x = y, y) {
console.log(x);
}
function b(x, y = x){
console.log(y);
}
a(undefined, 1) //TDZ es6的默認賦值是根據let實現的
b(1, undefined) //正確
複製代碼
//EventEmitter實例
const fs = require('fs');
const path = require('path');
const events = require('events');
//繼承events.EventEmitter實例
class MyEeventEmitter extends events.EventEmitter {}
let eventEmitterIns = new MyEeventEmitter();
//註冊事件
eventEmitterIns.addListener('read', function(data) {
console.log(data);
});
eventEmitterIns.on('read', function(data) {
console.log('這是觀察者模式');
});
//觸發事件 error first的方式
fs.readFile(path.resolve(__dirname,'date.html'), {encoding: 'utf-8'}, function(err, data){
eventEmitterIns.emit('read', data); //觸發事件
});
複製代碼
/**
* 相關api:
* on(type, callback) addListener(type, callback) emit(type, data) removeListener(type, callback) removeAllListeners() setMaxListenter(number)
* once(type,callback) 註冊的回調值執行一次,執行完後則刪除
* listenerCount() //註冊監聽數量
*/
//本身實現一個EventEmitter()
class EventEmitter {
constructor() {
//對象屬性
this.events = {};
this.maxListeners = 10;
}
//原型方法
//註冊回調 同一事件能夠有多個回調
on(type, callback) {
if (this.events[type]) {
if (this.events[type] > this.maxListeners) {
throw new TypeError('the listeners is full');
}
this.events[type].push(callback);
} else {
this.events[type] = [callback];
}
}
//觸發事件
emit(type, data) {
this.events[type] && this.events[type].forEach(cb => cb.call(this, data));
// for(let cb of this.events[type]){
// cb.call(this, data);
// }
}
//只調用一次的回調函數 調用後則刪除註冊的事件
once(type, callback) {
let wrapper = data => {
callback.call(this, data);
this.removeListener(type, wrapper); //執行後則刪除該註冊事件
};
this.on(type, wrapper);
}
//刪除事件
removeListener(type, callback) {
this.events[type] && (this.events[type] = this.events[type].filter(cb => callback !== cb));
}
//刪除全部註冊事件
removeAllListeners(type) {
if (type) {
delete this.events[type];
} else {
this.events = {};
}
}
setMaxListeners(number) {
this.maxListeners = number;
}
getMaxListeners() {
return this.maxListeners;
}
listeners(type) {
return this.events[type];
}
}
複製代碼
function thisTest(){
console.log(this.dog);
}
let obj1 = {
dog: 'wei'
}
let obj2 = {
dog: 'hello'
}
thisTest.bind(obj1).call(obj2);
//bind綁定的this沒法修改
複製代碼
//實現整個Promise的思路 共分爲4部走 實現Promise 實現原型方法 實現回調 實現靜態方法
//Promise構造階段
function Promise(fn) {
if (!(this instanceof Promise)) throw new TypeError('promise must be used new promise'); //必須new才能使用
if (typeof fn != 'function') throw new TypeError('the arguments must be the function'); //參數必須是函數
this.state = 0; // 0: pending 1: fullfilled 2:rejected 3:fullfilled 可是決議值時promise故需展開得到決議值
this.value = undefined; //決議值
this.handled = false; //是否處理回調
this.deffereds = []; //註冊的回調函數
doResolve(this, fn);
}
function deResolve(self, fn) {
//fn當即執行
let done = false;
try {
fn(
function(value) {
//外部resolve reject執行該函數 由此可看出只決議一次
if (done) return;
done = true;
resolve(self, value);
},
function(value) {
if (done) return;
done = true;
reject(self, value);
}
);
} catch (err) {
if (done) return; //只決議一次
done = true;
reject(self, err);
}
}
function reject(self, value) {
self.state = 2;
self.value = value;
finale(self);
}
function resolve(self, value) {
if (value === self) throw new TypeError('the promise value is not itself'); //決議值不能是本身
//thenable檢查 函數或對象有then方法 則認爲是promise
if (value && (typeof value == 'object' || typeof value === 'function')) {
if (value instanceof promise) {
//決議值時promise 則需展開
self.state = 3;
self.value = value;
finale(self);
return;
} else {
then = value.then;
if (typeof then === 'function') {
doResolve(self, then.bind(value)); //thenable promise則展開
}
}
}
self.state = 1;
self.value = value;
finale(self);
}
//異步回調
Promise.immediateFn =
(typeof immediate === 'function' &&
function(fn) {
setImmediate(fn);
}) ||
function(fn) {
setTimeout(fn, 0);
};
//拿到狀態執行回調
function finale(self) {
if (self.state === 2 && self.deferres.length === 0) {
//拒絕狀態且無註冊回調則不處理
Promise.immediateFn(function() {
if (!self.handled) {
console.log('the unhandle promise rejection');
}
});
}
for (let i = 0; i < self.deferreds.length; i++) {
//執行回調
handle(self, self.deferreds[i]);
}
self.deferreds = []; //處理完畢回調
}
//Promise 的原型方法
//返回一個新的promise 對象
function noop() {}
function Handle(self, onFullfilled, onRejected) {
//註冊的回調構造函數
this.onFullfilled = typeof onFullfilled == 'function' ? onFullfilled : null;
this.onRejected = typeof onRejected == 'function' ? onRejected : null;
this.promise = self;
}
Promise.prototype.then = function(onFullfilled, onRejected) {
let pro = new Promise(noop); //未決議
handle(this, new Handle(pro, onFullfilled, onRejected)); //註冊回調
return pro;
};
Promise.prototype['catch'] = function(onRejected) {
return this.then(null, onRejected);
};
//無論決議值成功失敗都將執行 且返回的promise的決議值爲原Promise的決議值
Promise.prototype['finally'] = function(callback) {
//最後執行
return this.then(
function(value) {
return Promise.resolve(callback()).then(function() {
return value;
});
},
function(value) {
return Promise.resolve(callback()).then(function() {
return value;
});
}
);
};
//處理回調
function handle(self, deffered) {
while (self.state === 3) {
self = self.value; //展開其promise值 得到其決議值
}
if (self.state === 0) {
self.deffereds.push(deffered);
return;
}
self.handled = true;
//異步執行回調
Promise.immediateFn(function() {
try {
let callback = self.state === 1 ? deffered.onFullfilled : deffered.onRejected;
if (callback === null) {
//默認無註冊回調 將新promise的決議值變爲原promise的決議值
self.state === 1 ? resolve(deffered.promise, self.value) : reject(defferd.promise, self.value);
}
let res = callback(self.value); //執行回調函數
self.state === 1 ? resolve(deffered.promise, res) : reject(deffered.promise, res);
} catch (err) {
reject(deffered.promise, err);
}
});
}
//靜態方法
function isArrayLike(arr) {
if (arr && typeof arr === 'object' && arr.length > 0 && isFinite(arr.length)) {
return true;
}
return false;
}
//返回promise 參數爲類數組 競賽決議 數組爲空則永不決議
Promise.race = function(arr) {
return new Promise(function(resolve, reject) {
if (!isArrayLike(arr)) {
throw new TypeError('the arguments must the array like');
}
let newArr = Array.from(arr);
newArr.forEach(function(val) {
Promise.resolve(val).then(resolve, reject);
});
});
};
Promise.all = function(arr) {
return new Promise(function(resolve, reject) {
if (!isArrayLike(arr)) {
throw new TypeError('the arguments must the array like');
}
let newArr = Array.from(arr); //可將類數組 或迭代對象 變爲數組
if (newArr.length === 0) resolve([]); //數組爲空當即決議
let count = newArr.length,
result = [];
function res(index, value) {
//判斷是否爲thenable
if (value && (typeof value === 'object' || typeof value === 'function')) {
let thenFunc = value.then;
if (typeof thenFunc == 'function') {
Promise.resolve(value).then(function(val) {
res(index, val);
}, reject);
}
}
result[index] = value;
count--;
if (!count) {
resolve(result);
}
}
for (let i = 0; i < newArr.length; i++) {
result(i, newArr[i]); //一一對應獲取相應promise的結果
}
});
};
Promise.reject = function(val) {
return new Promise(function(resolve, rejecte) {
reject(val);
});
};
//使其成爲真正的promise
Promise.resolve = function(val) {
if (val instanceof Promise) {
return val;
}
if (val && (typeof val === 'function' || typeof val === 'object')) {
let thenFunc = val.then;
if (typeof theFunc === 'function') {
return new Promise(thenFunc.bind(val));
}
}
return new Promise(function(resolve, reject) {
resolve(val);
});
};
複製代碼
//淺拷貝:只拷貝一層對象,經常使用方法有:手動實現拷貝一層Object.assign(target, ...source) slice() concat() ...展開運算符
function shollowClone(obj) {
if (!isCanCloneType(obj)) {
return obj;
}
let res = new obj.constructor(); //生成相應的拷貝對象
for (let key in obj) {
//訪問原型鏈上的屬性
if (obj.hasOwnProperty(key)) {
res[key] = obj[key];
}
}
return res;
}
// let a = 3;
// let b = { data: { node: 'html' }, func: function() {} };
// let c = [7, 7, [77]];
// //暫未考慮特殊狀況 基本類型的對象 function RegExp map set
// console.log(shollowClone(a));
// console.log(shollowClone(b));
// let d = shollowClone(c);
// console.log(d);
// d[2][0] = 66; //只拷貝一層
// console.log(c);
//深拷貝:基本數據類型不用拷貝,只能拷貝對象(數組,函數, set, map都算對象), 且基本類型的對象拷貝 和 function,RegExp須要特殊考慮
//遞歸進行深拷貝
/**
* 1. 考慮循環引用的問題,或者不一樣屬性引用同一屬性的問題,已經拷貝了的對象直接返回而不用在生成對象
* 2. 考慮特殊狀況: 基本類型的對象,Boolean, Number, String, RegExp, Date, set, map, function/箭頭函數
* 3. 不用遞歸作,將遞歸改變循環棧的形式 => 基本都是dfs深度優先遍歷
*/
let isCanCloneType = obj => (typeof obj == 'object' || typeof obj == 'function') && obj !== null; //代碼精簡
let calcType = obj => Object.prototype.toString.call(obj); // 返回對象的類型
let mapTypes = '[object Map]';
let setTypes = '[object Set]';
let numberTypes = '[object Number]';
let stringTyps = '[object String]';
let booleanTypes = '[object Boolean]';
let regexpTypes = '[object RegExp]';
let funcTypes = '[object Function]';
let symbolTypes = '[object Symbol]';
let dateTypes = '[object Date]';
//能夠處理的對象
let canTraverse = {
'[object Object]': true,
'[object Array]': true,
'[object Map]': true,
'[object Set]': true,
'[object Arguments]': true
};
//深拷貝特殊類型的對象
let cloneSepecialObj = (obj, objTypes) => {
let cons = obj.constructor;
switch (objTypes) {
case numberTypes:
return new Object(Number.prototypeof.valueOf.call(obj));
case stringTyps:
return new Object(String.prototypeof.valueOf.call(obj));
case booleanTypes:
return new Object(Boolean.prototypeof.valueOf.call(obj));
case symbolTypes:
return new Object(Symbol.prototypeof.valueOf.call(obj));
case dateTypes:
return new cons(obj);
case regexpTypes:
handleRegExp(obj);
case funcTypes:
handleFunc(obj);
default:
return new cons(obj);
}
};
function handleRegExp(obj) {
const { source, flags } = obj; // flag: g i m(多行匹配) source:正則表達字符串
return new obj.constructor(source, flags);
}
function handleFunc(obj) {
if (!obj.prototype) {
//若是是箭頭函數直接返回,由於箭頭函數每次都是新的值,而不會產生引用
return obj;
}
let param, body, func;
let paramReg = /(?<=\().*(?=\))\s*{/m; // ?<= 後行斷言 ?= 先行斷言
let bodyReg = /(?<=\{)(.|\n)*(?=})/m; // ?<= 後行斷言 ?= 先行斷言
func = obj.toString();
param = paramReg.exec(func); //匹配成功第一項返回成功的值 匹配失敗返回null
body = bodyReg.exec(func);
if (!body) return null;
if (param) {
return new Function(...param[0], body[0]);
} else {
return new Function(body[0]);
}
}
function deepClone(obj, map = new WeakMap()) {
if (!isCanCloneType(obj)) {
//判斷是否能夠拷貝
return obj;
}
if (map.get(obj)) {
return map.get(obj); //返回相對應的拷貝後對象
}
let res, objTypes;
objTypes = calcType(obj);
if (!canTraverse[objTypes]) {
return cloneSepecialObj(obj, objTypes);
}
res = new obj.constructor(); //生成相應的對象
map.set(obj, res); //保存對象 和其拷貝對象
if (objTypes === mapTypes) {
//若是是map則遍歷進行拷貝
obj.forEach((val, key) => {
res.set(deepClone(key, map), deepClone(val, map));
});
}
if (objTypes === setTypes) {
obj.forEach(val => {
res.add(deepClone(val, map));
});
}
//拷貝對象
for (let key in obj) {
if (obj.hasOwnProperty(key)) {
res[key] = deepClone(obj[key], map);
}
}
return res;
}
let a = 3;
console.log(deepClone(a));
let b = { data: { node: 'html' } };
//暫未考慮特殊狀況 基本類型的對象 function RegExp map set
let bb = deepClone(b);
bb.data.node = 'div';
console.log(b);
console.log(bb);
let c = [7, 7, [77]];
let d = deepClone(c);
d[2][0] = 66; //只拷貝一層
console.log(c);
console.log(d);
//map 強引用鍵值的對象 程序結束才垃圾回收 weakMap弱引用鍵值的對象,會則垃圾回收機制的時候進行回收
let map = new Map();
map.set(b, 'b');
map.set(c, 'c');
map.set('key', b);
let newMap = deepClone(map);
b.data.node = 'map';
console.log(map);
console.log(newMap);
//循環引用
var cir = { val: 3 };
cir.data = cir;
var newCir = deepClone(cir);
cir.data = { data: 4 };
console.log(cir);
console.log(newCir);
複製代碼
/**
* 柯里化函數:部分求值,傳遞給函數部分參數,並返回一個函數來接收剩餘的參數,分步求值,提升函數的複用性
*/
//1.經過獲取全部參數最後執行函數實現
function curring(fn){
let args = [].slice.call(arguments, 1);
let len = fn.length - args.length;
return function temp(){
len = len - arguments.length;
args.push(...arguments);
if(len > 0){
return temp;
}else{
return fn.apply(this, args);
}
}
}
function add(a, b){
return a + b;
}
//2.經過對原有函數進行包裝,最後傳遞參數進行執行
function subCurring(fn, ...args){ //對原有函數進行包裝
return function(...newArgs){
return fn.apply(this,args.concat(newArgs));
}
}
function curring(fn, length){
let len = length || fn.length;
let args = [];
return function (){
len = len - arguments.length;
args.push(...arguments);
if(len > 0){
return curring(subCurring.apply(this, [fn].concat(args)), len);
}else{
return fn.apply(this, args);
}
}
}
let foo = curring(add);
console.log(foo(1)(2));
複製代碼
//更改props 增長 刪除 修改 讀取 要傳遞給包裝組件的props
//獲取refs實例
//抽象state
function Hoc(WrappedComponent) {
return class extends React.Component {
constructor(props) {
super(props);
this.input = React.createRef(); //獲取實例
this.changeFunc = this.changeFunc.bind(this);
this.blur = this.blur.bind(this);
this.state = {
value: "抽象state, 複用組件邏輯",
onChange: this.changeFunc,
onBlur: this.blur
};
}
blur() {
alert(this.input.current.props.value);
}
changeFunc(event) {
event.stopPropagation(); //阻止冒泡
this.setState({
//抽象state
value: event.target.value
});
}
render() {
let style = {
backgroundColor: "red",
fontSize: 20
};
return (
<WrappedComponent
ref={this.input}
{...this.props}
{...this.state}
style={style}
/>
);
}
};
}
class WrappedComponent extends React.Component {
render() {
return <input type="input" {...this.props} />;
}
}
function App() {
let HocCom = Hoc(WrappedComponent);
return <HocCom value="hello world!" />;
}
複製代碼
import React from "react";
import { Component } from "react";
//反向繼承
//劫持渲染
class WrappedComponent extends Component {
constructor(props) {
super(props);
this.state = {
time: 2019
};
}
render() {
return (
<h3 login={false}>
hello world!
<p>paragraf</p>
{this.state.time}
</h3>
);
}
}
function Hoc(WrappedComponent) {
return class Test extends WrappedComponent {
render() {
//根據 props條件渲染
if (this.props.login) {
return super.render();
} else {
return <p>沒有登陸</p>;
}
}
};
}
//從新渲染樹 更改props更改子樹 從新渲染 重未更改包裝組件的props props是隻讀的不可更改
function HocII(WrappedComponentI) {
return class Test extends WrappedComponentI {
render() {
let elementTree = super.render();
let newProps = {
style: { backgroundColor: "red" },
value: "hello 反向繼承"
};
return (
<div>
{this.state.time}
{React.cloneElement( elementTree, newProps, // <input value="hahahh" />
elementTree.props.children )}
</div>
);
}
};
}
export default HocII(WrappedComponent);
複製代碼
function fn() {
name = "你我貸"
}
console.log(name) //name沒法回收
複製代碼
//[1, [2, 3, [4, 5]]] ------> [1, 2, 3, 4, 5] 遞歸法扁平化數組,求數組深度
function flatten(arr, res){
let count = 1;
for(let value of arr){
if(Array.isArray(value)){
count = flatten(value, res) + 1;
}else{
res.push(value);
}
}
return count;
}
let res = [];
let count = flatten([1, [2, 3, [4, 5]],10], res);
console.log(res, count);
//法二 優先選擇
function flattenI(arr){
let res = [];
for(let value of arr){
if(Array.isArray(value)){
res = res.concat(flattenI(value));
}else{
res.push(value);
}
}
return res;
}
console.log(flattenI([1, [2, 3, [4, 5]],10]));
//法三 reduce
function flattenII(arr){
return arr.reduce(function(pre, current){
return pre.concat(Array.isArray(current) ? flattenII(current) : current);
},[]);
}
console.log(flattenII([1, [2, 3, [4, 5]],10]));
//法四 toString()/join()
function flattenIII(arr){
return arr.toString().split(',').map(value => Number(value));
}
console.log(flattenIII([1, [2, 3, [4, 5]],10]));
function flattenIIII(arr){
return arr.join(',').split(',').map(value => parseInt(value));
}
console.log(flattenIIII([1, [2, 3, [4, 5]],10]));
//...展開運算符,展開二維數組
function flattenIIIII(arr){
while(arr.find(value => Array.isArray(value))){
arr = [].concat(...arr);
}
return arr;
}
console.log(flattenIIIII([1, [2, 3, [4, 5]],10]));
//展開方法
function faltWays(arr){
return arr.flat(Infinity); // 扁平深度爲無窮 僅瀏覽器中存在該方法
}
//正則表達式匹配
function faltReg(arr){
return JSON.stringify(arr).replace(/(\[|\])/g, '').split(',').map(val => Number(val));
}
//JSON.stringify和正則表達式匹配最後轉化爲整形
function faltRegI(arr){
let str = JSON.stringify(arr).replace(/(\[|\])/g, '');
str = '[' + str + ']';
return JSON.parse(str).map(val => parseInt(val));
}
複製代碼
/**
* new: 生成一個對象,對象進行原型鏈委託,this指向這個對象,若是返回值是對象(不是null)或者函數,則返回自身,不然返回生成的對象
*/
function newFunc(fn, ...args) {
if (typeof fn != 'function') {
throw new TypeError(`the first param is not a function`);
}
let obj = Object.create(fn.prototype); //完成2步
let res = fn.apply(obj, args);
let isObject = typeof res == 'object' && res !== null;
let isFunction = typeof res == 'function';
return isObject || isFunction ? res : obj;
}
複製代碼
//遞歸作深拷貝
function deepClone(obj, map = new WeakMap()){
if(!isCanCloneType(obj)){
return obj;
}
let objTypes, root, stack = [];
objTypes = calcType(obj);
if(!canTraverse[objTypes]){
return cloneSpecialObj(obj, objTypes);
}
root = new obj.constructor();
stack[0] = { //初始化首棧 key data 表明當前父元素須要拷貝的下一個對象
parent: root,
key: undefined,
data: obj
}
while(stack.length){
let node = stack.pop();
let {parent, key, data} = node;
let res;
if(key !== undefined){
res = parent[key] = new data.constructor(); //若是存在下一層級,則進行關聯,指向同一地址,進行拷貝,沒有則拷貝在父元素上
}else{
res = parent;
}
if(map.get(data)){ //循環引用
parent[key] = map.get(data);
continue;
}
map.set(data, res);
for(let key in data){
if(obj.hasOwnProperty(key)){
if(isCanCloneType(data[key])){
stack.push({
parent: res,
key: key,
data: data[key]
})
} else{
res[key] = data[key];
}
}
}
}
return root;
}
複製代碼
({
x: 10,
foo: function () {
function bar() {
console.log(x); //undefined
console.log(y); // 30
console.log(this.x); //20
}
with (this) {
var x = 20; // 同名屬性進行修改 聲明提早
var y = 30; //局部變量值改變 聲明提早
bar.call(this);
}
}
}).foo();
複製代碼
function ins(obj, obj2) {
if (typeof obj2 !== 'function') {
throw new TypeError('the second param must be function');
}
let obj1 = obj;
while (obj1) {
if (obj1.__proto__ === obj2.prototype) {
return true;
}
obj1 = obj1.__proto__;
}
return false;
}
複製代碼