国产xxxx99真实实拍_久久不雅视频_高清韩国a级特黄毛片_嗯老师别我我受不了了小说

資訊專欄INFORMATION COLUMN

【Step-By-Step】高頻面試題深入解析 / 周刊07

superw / 1044人閱讀

摘要:復(fù)雜數(shù)據(jù)類型變量,賦值給之后,只讀引用與關(guān)聯(lián),和中存儲的是同一個對象的堆內(nèi)存地址,當(dāng)這個對象的值發(fā)生改變時,此時的值也會發(fā)生變化。

不積跬步無以至千里。

關(guān)于【Step-By-Step】
Step-By-Step (點擊進(jìn)入項目) 是我于 2019-05-20 開始的一個項目,每個工作日發(fā)布一道面試題。

每個周末我會仔細(xì)閱讀大家的答案,整理最一份較優(yōu)答案出來,因本人水平有限,有誤的地方,大家及時指正。

如果想 加群 學(xué)習(xí),可以通過文末的公眾號,添加我為好友。

更多優(yōu)質(zhì)文章可戳: https://github.com/YvetteLau/...

__

本周面試題一覽:

實現(xiàn)一個 JSON.stringify

實現(xiàn)一個 JSON.parse

實現(xiàn)一個觀察者模式

使用CSS讓一個元素水平垂直居中有哪些方式

ES6模塊和CommonJS模塊有哪些差異?

31. 實現(xiàn)一個 JSON.stringify

JSON.stringify([, replacer [, space]) 方法是將一個JavaScript值(對象或者數(shù)組)轉(zhuǎn)換為一個 JSON字符串。此處模擬實現(xiàn),不考慮可選的第二個參數(shù) replacer 和第三個參數(shù) space,如果對這兩個參數(shù)的作用還不了解,建議閱讀 MDN 文檔。

JSON.stringify() 將值轉(zhuǎn)換成對應(yīng)的 JSON 格式:

基本數(shù)據(jù)類型:

undefined 轉(zhuǎn)換之后仍是 undefined(類型也是 undefined)

boolean 值轉(zhuǎn)換之后是字符串 "false"/"true"

number 類型(除了 NaNInfinity)轉(zhuǎn)換之后是字符串類型的數(shù)值

symbol 轉(zhuǎn)換之后是 undefined

null 轉(zhuǎn)換之后是字符串 "null"

string 轉(zhuǎn)換之后仍是string

NaNInfinity 轉(zhuǎn)換之后是字符串 "null"

如果是函數(shù)類型

轉(zhuǎn)換之后是 undefined

如果是對象類型(非函數(shù))

如果有 toJSON() 方法,那么序列化 toJSON() 的返回值。

如果是一個數(shù)組

- 如果屬性值中出現(xiàn)了 `undefined`、任意的函數(shù)以及 `symbol`,轉(zhuǎn)換成字符串 `"null"`

如果是 RegExp 對象。

 返回 `{}` (類型是 string)

如果是 Date 對象,返回 DatetoJSON 字符串值

如果是普通對象;

 - 如果屬性值中出現(xiàn)了 `undefined`、任意的函數(shù)以及 symbol 值,忽略。
 - 所有以 `symbol` 為屬性鍵的屬性都會被完全忽略掉。

對包含循環(huán)引用的對象(對象之間相互引用,形成無限循環(huán))執(zhí)行此方法,會拋出錯誤。

模擬實現(xiàn)
function jsonStringify(data) {
    let dataType = typeof data;
    if (dataType !== "object") {
        let result = data;
        //data 可能是 string/number/null/undefined/boolean
        if (Number.isNaN(data) || data === Infinity) {
            //NaN 和 Infinity 序列化返回 "null"
            result = "null";
        } else if (dataType === "function" || dataType === "undefined" || dataType === "symbol") {
            //function 、undefined 、symbol 序列化返回 undefined
            return undefined;
        } else if (dataType === "string") {
            result = """ + data + """;
        }
        //boolean 返回 String()
        return String(result);
    } else if (dataType === "object") {
        if (data === null) {
            return "null";
        } else if (data.toJSON && typeof data.toJSON === "function") {
            return jsonStringify(data.toJSON());
        } else if (data instanceof Array) {
            let result = [];
            //如果是數(shù)組
            //toJSON 方法可以存在于原型鏈中
            data.forEach((item, index) => {
                if (typeof item === "undefined" || typeof item === "function" || typeof item === "symbol") {
                    result[index] = "null";
                } else {
                    result[index] = jsonStringify(item);
                }
            });
            result = "[" + result + "]";
            return result.replace(/"/g, """);

        } else {
            //普通對象
            /**
             * 循環(huán)引用拋錯(暫未檢測,循環(huán)引用時,堆棧溢出)
             * symbol key 忽略
             * undefined、函數(shù)、symbol 為屬性值,被忽略
             */
            let result = [];
            Object.keys(data).forEach((item, index) => {
                if (typeof item !== "symbol") {
                    //key 如果是symbol對象,忽略
                    if (data[item] !== undefined && typeof data[item] !== "function"
                        && typeof data[item] !== "symbol") {
                        //鍵值如果是 undefined、函數(shù)、symbol 為屬性值,忽略
                        result.push(""" + item + """ + ":" + jsonStringify(data[item]));
                    }
                }
            });
            return ("{" + result + "}").replace(/"/g, """);
        }
    }
}

測試代碼:

let sym = Symbol(10);
console.log(jsonStringify(sym) === JSON.stringify(sym));
let nul = null;
console.log(jsonStringify(nul) === JSON.stringify(nul));
let und = undefined;
console.log(jsonStringify(undefined) === JSON.stringify(undefined));
let boo = false;
console.log(jsonStringify(boo) === JSON.stringify(boo));
let nan = NaN;
console.log(jsonStringify(nan) === JSON.stringify(nan));
let inf = Infinity;
console.log(jsonStringify(Infinity) === JSON.stringify(Infinity));
let str = "hello";
console.log(jsonStringify(str) === JSON.stringify(str));
let reg = new RegExp("w");
console.log(jsonStringify(reg) === JSON.stringify(reg));
let date = new Date();
console.log(jsonStringify(date) === JSON.stringify(date));
let obj = {
    name: "劉小夕",
    age: 22,
    hobbie: ["coding", "writing"],
    date: new Date(),
    unq: Symbol(10),
    sayHello: function () {
        console.log("hello")
    },
    more: {
        brother: "Star",
        age: 20,
        hobbie: [null],
        info: {
            money: undefined,
            job: null,
            others: []
        }
    }
}
console.log(jsonStringify(obj) === JSON.stringify(obj));


function SuperType(name, age) {
    this.name = name;
    this.age = age;
}
let per = new SuperType("小姐姐", 20);
console.log(jsonStringify(per) === JSON.stringify(per));

function SubType(info) {
    this.info = info;
}
SubType.prototype.toJSON = function () {
    return {
        name: "錢錢錢",
        mount: "many",
        say: function () {
            console.log("我偏不說!");
        },
        more: null,
        reg: new RegExp("w")
    }
}
let sub = new SubType("hi");
console.log(jsonStringify(sub) === JSON.stringify(sub));
let map = new Map();
map.set("name", "小姐姐");
console.log(jsonStringify(map) === JSON.stringify(map));
let set = new Set([1, 2, 3, 4, 5, 1, 2, 3]);
console.log(jsonStringify(set) === JSON.stringify(set));
32. 實現(xiàn)一個 JSON.parse

JSON.parse(JSON.parse(text[, reviver]) 方法用來解析JSON字符串,構(gòu)造由字符串描述的JavaScript值或?qū)ο蟆L峁┛蛇x的reviver函數(shù)用以在返回之前對所得到的對象執(zhí)行變換。此處模擬實現(xiàn),不考慮可選的第二個參數(shù) reviver ,如果對這個參數(shù)的作用還不了解,建議閱讀 MDN 文檔。

第一種方式 eval

最簡單,最直觀的方式就是調(diào)用 eval

var json = "{"name":"小姐姐", "age":20}";
var obj = eval("(" + json + ")");  // obj 就是 json 反序列化之后得到的對象

直接調(diào)用 eval 存在 XSS 漏洞,數(shù)據(jù)中可能不是 json 數(shù)據(jù),而是可執(zhí)行的 JavaScript 代碼。因此,在調(diào)用 eval 之前,需要對數(shù)據(jù)進(jìn)行校驗。

var rx_one = /^[],:{}s]*$/;
var rx_two = /(?:["/bfnrt]|u[0-9a-fA-F]{4})/g;
var rx_three = /"[^"

]*"|true|false|null|-?d+(?:.d*)?(?:[eE][+-]?d+)?/g;
var rx_four = /(?:^|:|,)(?:s*[)+/g;

if (
    rx_one.test(
        json
            .replace(rx_two, "@")
            .replace(rx_three, "]")
            .replace(rx_four, "")
    )
) {
    var obj = eval("(" +json + ")");
}

JSON 是 JS 的子集,可以直接交給 eval 運行。

第二種方式 new Function

Functioneval 有相同的字符串參數(shù)特性。

var json = "{"name":"小姐姐", "age":20}";
var obj = (new Function("return " + json))();
33. 實現(xiàn)一個觀察者模式

觀察者模式定義了對象間的一種一對多的依賴關(guān)系,當(dāng)一個對象的狀態(tài)發(fā)生改變時,所有依賴于它的對象都將得到通知,并自動更新。觀察者模式屬于行為型模式,行為型模式關(guān)注的是對象之間的通訊,觀察者模式就是觀察者和被觀察者之間的通訊。

觀察者(Observer)直接訂閱(Subscribe)主題(Subject),而當(dāng)主題被激活的時候,會觸發(fā)(Fire Event)觀察者里的事件。

    //有一家獵人工會,其中每個獵人都具有發(fā)布任務(wù)(publish),訂閱任務(wù)(subscribe)的功能
    //他們都有一個訂閱列表來記錄誰訂閱了自己
    //定義一個獵人類
    //包括姓名,級別,訂閱列表
    function Hunter(name, level){
        this.name = name
        this.level = level
        this.list = []
    }
    Hunter.prototype.publish = function (money){
        console.log(this.level + "獵人" + this.name + "尋求幫助")
        this.list.forEach(function(item, index){
            item(money)
        })
    }
    Hunter.prototype.subscribe = function (targrt, fn){
        console.log(this.level + "獵人" + this.name + "訂閱了" + targrt.name)
        targrt.list.push(fn)
    }
    
    //獵人工會走來了幾個獵人
    let hunterMing = new Hunter("小明", "黃金")
    let hunterJin = new Hunter("小金", "白銀")
    let hunterZhang = new Hunter("小張", "黃金")
    let hunterPeter = new Hunter("Peter", "青銅")
    
    //Peter等級較低,可能需要幫助,所以小明,小金,小張都訂閱了Peter
    hunterMing.subscribe(hunterPeter, function(money){
        console.log("小明表示:" + (money > 200 ? "" : "暫時很忙,不能") + "給予幫助")
    });
    hunterJin.subscribe(hunterPeter, function(){
        console.log("小金表示:給予幫助")
    });
    hunterZhang.subscribe(hunterPeter, function(){
        console.log("小張表示:給予幫助")
    });
    
    //Peter遇到困難,賞金198尋求幫助
    hunterPeter.publish(198);
    
    //獵人們(觀察者)關(guān)聯(lián)他們感興趣的獵人(目標(biāo)對象),如Peter,當(dāng)Peter有困難時,會自動通知給他們(觀察者)
34. 使用 CSS 讓一個元素水平垂直居中

父元素 .container

子元素 .box

利用 flex 布局
/* 無需知道被居中元素的寬高 */
.container {
    display: flex;
    align-items: center;
    justify-content: center;
}
子元素是單行文本

設(shè)置父元素的 text-alignline-height = height

.container {
    height: 100px;
    line-height: 100px;
    text-align: center;
}
利用 absolute + transform
/* 無需知道被居中元素的寬高 */
/* 設(shè)置父元素非 `static` 定位 */
.container {
    position: relative;
}
/* 子元素絕對定位,使用 translate的好處是無需知道子元素的寬高 */
/* 如果知道寬高,也可以使用 margin 設(shè)置 */
.box {
    position: absolute;
    left: -50%;
    top: -50%;
    transform: translate(-50%, -50%);
}
利用 grid 布局
/* 無需知道被居中元素的寬高 */
.container {
    display: grid;
}
.box {
    justify-self: center; 
    align-self: center;
}
利用絕對定位和 margin:auto
/* 無需知道被居中元素的寬高 */
.box {
    position: absolute;
    left: 0;
    top: 0;
    right: 0;
    bottom: 0;
    margin: auto;
}
.container {
    position: relative;
}
35. ES6模塊和 CommonJS 模塊有哪些差異?
1. CommonJS 模塊是運行時加載,ES6模塊是編譯時輸出接口。

ES6模塊在編譯時,就能確定模塊的依賴關(guān)系,以及輸入和輸出的變量。ES6 模塊不是對象,它的對外接口只是一種靜態(tài)定義,在代碼靜態(tài)解析階段就會生成。

CommonJS 加載的是一個對象,該對象只有在腳本運行完才會生成。

2. CommonJS 模塊輸出的是一個值的拷貝,ES6模塊輸出的是值的引用。
- `CommonJS` 輸出的是一個值的拷貝(注意基本數(shù)據(jù)類型/復(fù)雜數(shù)據(jù)類型)
    
- ES6 模塊是動態(tài)引用,并且不會緩存值,模塊里面的變量綁定其所在的模塊。

CommonJS 模塊輸出的是值的拷貝。

模塊輸出的值是基本數(shù)據(jù)類型,模塊內(nèi)部的變化就影響不到這個值。
//name.js
let name = "William";
setTimeout(() => { name = "Yvette"; }, 300);
module.exports = name;

//index.js
const name = require("./name");
console.log(name); //William
//name.js 模塊加載后,它的內(nèi)部變化就影響不到 name
//name 是一個基本數(shù)據(jù)類型。將其復(fù)制出一份之后,二者之間互不影響。
setTimeout(() => console.log(name), 500); //William
模塊輸出的值是復(fù)雜數(shù)據(jù)類型

模塊輸出的是對象,屬性值是簡單數(shù)據(jù)類型時:

//name.js
let name = "William";
setTimeout(() => { name = "Yvette"; }, 300);
module.exports = { name };

//index.js
const { name } = require("./name");
console.log(name); //William
//name 是一個原始類型的值,會被緩存。
setTimeout(() => console.log(name), 500); //William
模塊輸出的是對象:
//name.js
let name = "William";
let hobbies = ["coding"];
setTimeout(() => { 
    name = "Yvette";
    hobbies.push("reading");
}, 300);
module.exports = { name, hobbies };

//index.js
const { name, hobbies } = require("./name");
console.log(name); //William
console.log(hobbies); //["coding"]
/*
 * name 的值沒有受到影響,因為 {name: name} 屬性值 name 存的是個字符串
 *     300ms后 name 變量重新賦值,但是不會影響 {name: name}
 * 
 * hobbies 的值會被影響,因為 {hobbies: hobbies} 屬性值 hobbies 中存的是
 *     數(shù)組的堆內(nèi)存地址,因此當(dāng) hobbies 對象的值被改變時,存在棧內(nèi)存中的地址并
       沒有發(fā)生變化,因此 hoobies 對象值的改變會影響 {hobbies: hobbies} 
 * xx = { name, hobbies } 也因此改變 (復(fù)雜數(shù)據(jù)類型,拷貝的棧內(nèi)存中存的地址)  
 */
setTimeout(() => {
    console.log(name);//William
    console.log(hobbies);//["coding", "reading"]
}, 500);

ES6 模塊的運行機制與 CommonJS 不一樣。JS 引擎對腳本靜態(tài)分析的時候,遇到模塊加載命令 import ,就會生成一個只讀引用。等到腳本真正執(zhí)行時,再根據(jù)這個只讀引用,到被加載的那個模塊里面去取值。

//name.js
let name = "William";
setTimeout(() => { name = "Yvette"; hobbies.push("writing"); }, 300);
export { name };
export var hobbies = ["coding"];

//index.js
import { name, hobbies } from "./name";
console.log(name, hobbies); //William ["coding"]
//name 和 hobbie 都會被模塊內(nèi)部的變化所影響
setTimeout(() => {
    console.log(name, hobbies); //Yvette ["coding", "writing"]
}, 500); //Yvette

ES6 模塊是動態(tài)引用,并且不會緩存值,模塊里面的變量綁定其所在的模塊。因此上面的例子也很容易理解。

那么 export default 導(dǎo)出是什么情況呢?

//name.js
let name = "William";
let hobbies = ["coding"]
setTimeout(() => { name = "Yvette"; hobbies.push("writing"); }, 300);
export default { name, hobbies };

//index.js
import info from "./name";
console.log(info.name, info.hobbies); //William ["coding"]
//name 不會被模塊內(nèi)部的變化所影響
//hobbie 會被模塊內(nèi)部的變化所影響
setTimeout(() => {
    console.log(info.name, info.hobbies); //William ["coding", "writing"]
}, 500); //Yvette

一起看一下為什么。

export default 可以理解為將變量賦值給 default,最后導(dǎo)出 default (僅是方便理解,不代表最終的實現(xiàn),如果對這塊感興趣,可以閱讀 webpack 編譯出來的代碼)。

基礎(chǔ)類型變量 name, 賦值給 default 之后,只讀引用與 default 關(guān)聯(lián),此時原變量 name 的任何修改都與 default 無關(guān)。

復(fù)雜數(shù)據(jù)類型變量 hobbies,賦值給 default之后,只讀引用與 default 關(guān)聯(lián),defaulthobbies 中存儲的是同一個對象的堆內(nèi)存地址,當(dāng)這個對象的值發(fā)生改變時,此時 default 的值也會發(fā)生變化。

3. ES6 模塊自動采用嚴(yán)格模式,無論模塊頭部是否寫了 "use strict";
4. require 可以做動態(tài)加載,import 語句做不到,import 語句必須位于頂層作用域中。
5. ES6 模塊的輸入變量是只讀的,不能對其進(jìn)行重新賦值
import name from "./name";
name = "Star"; //拋錯
6. 當(dāng)使用require命令加載某個模塊時,就會運行整個模塊的代碼。
7. 當(dāng)使用require命令加載同一個模塊時,不會再執(zhí)行該模塊,而是取到緩存之中的值。也就是說,CommonJS模塊無論加載多少次,都只會在第一次加載時運行一次,以后再加載,就返回第一次運行的結(jié)果,除非手動清除系統(tǒng)緩存。
參考文章:

[1] 珠峰架構(gòu)課(墻裂推薦)
[2] JSON.parse三種實現(xiàn)方式
[3] ES6 文檔
[4] JSON-js
[5] CommonJS模塊和ES6模塊的區(qū)別
[6] 發(fā)布訂閱模式與觀察者模式

謝謝各位小伙伴愿意花費寶貴的時間閱讀本文,如果本文給了您一點幫助或者是啟發(fā),請不要吝嗇你的贊和Star,您的肯定是我前進(jìn)的最大動力。 https://github.com/YvetteLau/...

關(guān)注公眾號,加入技術(shù)交流群。

文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請注明本文地址:http://m.specialneedsforspecialkids.com/yun/105501.html

相關(guān)文章

  • Step-By-Step高頻面試深入解析 / 周刊05

    摘要:關(guān)于點擊進(jìn)入項目是我于開始的一個項目,每個工作日發(fā)布一道面試題。那個率先改變的實例的返回值,就傳遞給的回調(diào)函數(shù)。通過插入標(biāo)簽的方式來實現(xiàn)跨域,參數(shù)只能通過傳入,僅能支持請求。因此清除浮動,只需要觸發(fā)一個即可。 關(guān)于【Step-By-Step】 Step-By-Step (點擊進(jìn)入項目) 是我于 2019-05-20 開始的一個項目,每個工作日發(fā)布一道面試題。每個周末我會仔細(xì)閱讀大家的...

    xiangchaobin 評論0 收藏0
  • Step-By-Step高頻面試深入解析 / 周刊04

    摘要:關(guān)于點擊進(jìn)入項目是我于開始的一個項目,每個工作日發(fā)布一道面試題。的狀態(tài)由決定,分成以下兩種情況只有的狀態(tài)都變成,的狀態(tài)才會變成,此時的返回值組成一個數(shù)組,傳遞給的回調(diào)函數(shù)。 關(guān)于【Step-By-Step】 Step-By-Step (點擊進(jìn)入項目) 是我于 2019-05-20 開始的一個項目,每個工作日發(fā)布一道面試題。每個周末我會仔細(xì)閱讀大家的答案,整理最一份較優(yōu)答案出來,因本人...

    youkede 評論0 收藏0

發(fā)表評論

0條評論

最新活動
閱讀需要支付1元查看
<