成人无码视频,亚洲精品久久久久av无码,午夜精品久久久久久毛片,亚洲 中文字幕 日韩 无码

資訊專欄INFORMATION COLUMN

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

superw / 1114人閱讀

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

不積跬步無以至千里。

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

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

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

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

__

本周面試題一覽:

實(shí)現(xiàn)一個(gè) JSON.stringify

實(shí)現(xiàn)一個(gè) JSON.parse

實(shí)現(xiàn)一個(gè)觀察者模式

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

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

31. 實(shí)現(xiàn)一個(gè) JSON.stringify

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

JSON.stringify() 將值轉(zhuǎn)換成對(duì)應(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

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

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

如果是一個(gè)數(shù)組

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

如果是 RegExp 對(duì)象。

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

如果是 Date 對(duì)象,返回 DatetoJSON 字符串值

如果是普通對(duì)象;

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

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

模擬實(shí)現(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 {
            //普通對(duì)象
            /**
             * 循環(huán)引用拋錯(cuò)(暫未檢測,循環(huán)引用時(shí),堆棧溢出)
             * symbol key 忽略
             * undefined、函數(shù)、symbol 為屬性值,被忽略
             */
            let result = [];
            Object.keys(data).forEach((item, index) => {
                if (typeof item !== "symbol") {
                    //key 如果是symbol對(duì)象,忽略
                    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. 實(shí)現(xiàn)一個(gè) JSON.parse

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

第一種方式 eval

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

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

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

第二種方式 new Function

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

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

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

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

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

父元素 .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;
}
/* 子元素絕對(duì)定位,使用 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;
}
利用絕對(duì)定位和 margin:auto
/* 無需知道被居中元素的寬高 */
.box {
    position: absolute;
    left: 0;
    top: 0;
    right: 0;
    bottom: 0;
    margin: auto;
}
.container {
    position: relative;
}
35. ES6模塊和 CommonJS 模塊有哪些差異?
1. CommonJS 模塊是運(yùn)行時(shí)加載,ES6模塊是編譯時(shí)輸出接口。

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

CommonJS 加載的是一個(gè)對(duì)象,該對(duì)象只有在腳本運(yùn)行完才會(huì)生成。

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

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

模塊輸出的值是基本數(shù)據(jù)類型,模塊內(nèi)部的變化就影響不到這個(gè)值。
//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 是一個(gè)基本數(shù)據(jù)類型。將其復(fù)制出一份之后,二者之間互不影響。
setTimeout(() => console.log(name), 500); //William
模塊輸出的值是復(fù)雜數(shù)據(jù)類型

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

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

//index.js
const { name } = require("./name");
console.log(name); //William
//name 是一個(gè)原始類型的值,會(huì)被緩存。
setTimeout(() => console.log(name), 500); //William
模塊輸出的是對(duì)象:
//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 的值沒有受到影響,因?yàn)?{name: name} 屬性值 name 存的是個(gè)字符串
 *     300ms后 name 變量重新賦值,但是不會(huì)影響 {name: name}
 * 
 * hobbies 的值會(huì)被影響,因?yàn)?{hobbies: hobbies} 屬性值 hobbies 中存的是
 *     數(shù)組的堆內(nèi)存地址,因此當(dāng) hobbies 對(duì)象的值被改變時(shí),存在棧內(nèi)存中的地址并
       沒有發(fā)生變化,因此 hoobies 對(duì)象值的改變會(huì)影響 {hobbies: hobbies} 
 * xx = { name, hobbies } 也因此改變 (復(fù)雜數(shù)據(jù)類型,拷貝的棧內(nèi)存中存的地址)  
 */
setTimeout(() => {
    console.log(name);//William
    console.log(hobbies);//["coding", "reading"]
}, 500);

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

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

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

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

一起看一下為什么。

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

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

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

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

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

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

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

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

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

相關(guān)文章

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

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

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

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

    youkede 評(píng)論0 收藏0

發(fā)表評(píng)論

0條評(píng)論

閱讀需要支付1元查看
<