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

資訊專欄INFORMATION COLUMN

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

superw / 1039人閱讀

摘要:復雜數據類型變量,賦值給之后,只讀引用與關聯,和中存儲的是同一個對象的堆內存地址,當這個對象的值發生改變時,此時的值也會發生變化。

不積跬步無以至千里。

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

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

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

更多優質文章可戳: https://github.com/YvetteLau/...

__

本周面試題一覽:

實現一個 JSON.stringify

實現一個 JSON.parse

實現一個觀察者模式

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

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

31. 實現一個 JSON.stringify

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

JSON.stringify() 將值轉換成對應的 JSON 格式:

基本數據類型:

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

boolean 值轉換之后是字符串 "false"/"true"

number 類型(除了 NaNInfinity)轉換之后是字符串類型的數值

symbol 轉換之后是 undefined

null 轉換之后是字符串 "null"

string 轉換之后仍是string

NaNInfinity 轉換之后是字符串 "null"

如果是函數類型

轉換之后是 undefined

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

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

如果是一個數組

- 如果屬性值中出現了 `undefined`、任意的函數以及 `symbol`,轉換成字符串 `"null"`

如果是 RegExp 對象。

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

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

如果是普通對象;

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

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

模擬實現
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 = [];
            //如果是數組
            //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 {
            //普通對象
            /**
             * 循環引用拋錯(暫未檢測,循環引用時,堆棧溢出)
             * symbol key 忽略
             * undefined、函數、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、函數、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. 實現一個 JSON.parse

JSON.parse(JSON.parse(text[, reviver]) 方法用來解析JSON字符串,構造由字符串描述的JavaScript值或對象。提供可選的reviver函數用以在返回之前對所得到的對象執行變換。此處模擬實現,不考慮可選的第二個參數 reviver ,如果對這個參數的作用還不了解,建議閱讀 MDN 文檔。

第一種方式 eval

最簡單,最直觀的方式就是調用 eval

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

直接調用 eval 存在 XSS 漏洞,數據中可能不是 json 數據,而是可執行的 JavaScript 代碼。因此,在調用 eval 之前,需要對數據進行校驗。

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 有相同的字符串參數特性。

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

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

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

    //有一家獵人工會,其中每個獵人都具有發布任務(publish),訂閱任務(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);
    
    //獵人們(觀察者)關聯他們感興趣的獵人(目標對象),如Peter,當Peter有困難時,會自動通知給他們(觀察者)
34. 使用 CSS 讓一個元素水平垂直居中

父元素 .container

子元素 .box

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

設置父元素的 text-alignline-height = height

.container {
    height: 100px;
    line-height: 100px;
    text-align: center;
}
利用 absolute + transform
/* 無需知道被居中元素的寬高 */
/* 設置父元素非 `static` 定位 */
.container {
    position: relative;
}
/* 子元素絕對定位,使用 translate的好處是無需知道子元素的寬高 */
/* 如果知道寬高,也可以使用 margin 設置 */
.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模塊在編譯時,就能確定模塊的依賴關系,以及輸入和輸出的變量。ES6 模塊不是對象,它的對外接口只是一種靜態定義,在代碼靜態解析階段就會生成。

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

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

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

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

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

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

//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 中存的是
 *     數組的堆內存地址,因此當 hobbies 對象的值被改變時,存在棧內存中的地址并
       沒有發生變化,因此 hoobies 對象值的改變會影響 {hobbies: hobbies} 
 * xx = { name, hobbies } 也因此改變 (復雜數據類型,拷貝的棧內存中存的地址)  
 */
setTimeout(() => {
    console.log(name);//William
    console.log(hobbies);//["coding", "reading"]
}, 500);

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

//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 都會被模塊內部的變化所影響
setTimeout(() => {
    console.log(name, hobbies); //Yvette ["coding", "writing"]
}, 500); //Yvette

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

那么 export default 導出是什么情況呢?

//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 不會被模塊內部的變化所影響
//hobbie 會被模塊內部的變化所影響
setTimeout(() => {
    console.log(info.name, info.hobbies); //William ["coding", "writing"]
}, 500); //Yvette

一起看一下為什么。

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

基礎類型變量 name, 賦值給 default 之后,只讀引用與 default 關聯,此時原變量 name 的任何修改都與 default 無關。

復雜數據類型變量 hobbies,賦值給 default之后,只讀引用與 default 關聯,defaulthobbies 中存儲的是同一個對象的堆內存地址,當這個對象的值發生改變時,此時 default 的值也會發生變化。

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

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

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

關注公眾號,加入技術交流群。

文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。

轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/105501.html

相關文章

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

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

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

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

    youkede 評論0 收藏0

發表評論

0條評論

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