前端面試題知識點(diǎn)(TT)

1,arguments

var length = 10;
function fn() {
    alert(this.length);
}
var obj = {
    length: 5,
    method: function (fn) {
        fn(); //?
        arguments[0](); //?
    },
    fn: function() {
        console.log(this.length);
    }
};
obj.method(fn); 
obj.fn();

arguments[0](); == arguments.fn();

2,字符串原型

var a = "123";
a.duplicate(); // "123123"

String.prototype.duplicate = () =>  {
    return this + this;
}

null - (-1) // ?   1

3,css line-height區(qū)別

line-height:26px;
line-height:1.5;
line-height:150%;
line-height:1.5rem;

4,字符模版

const template = "My name is ${name},I'm from ${city}";
const result = sprintf(template, {
    name: 'Zhang',
    city: 'FuJian',
});
console.log(result); // My name is Zhang,I'm from FuJian


const sprintf = (str, data) => (
    Object.keys(data).reduce((prev, cur) => {
        let reg = new RegExp('\\$\\{' + cur + '\\}', 'g');
        return prev.replace(reg, data[cur]);
    }, str);
);

5,節(jié)流

// func是用戶傳入需要防抖的函數(shù)
// wait是等待時(shí)間
const throttle = (func, wait = 50) => {
  // 上一次執(zhí)行該函數(shù)的時(shí)間
  let lastTime = 0
  return function(...args) {
    // 當(dāng)前時(shí)間
    let now = +new Date()
    // 將當(dāng)前時(shí)間和上一次執(zhí)行函數(shù)時(shí)間對比
    // 如果差值大于設(shè)置的等待時(shí)間就執(zhí)行函數(shù)
    if (now - lastTime > wait) {
      lastTime = now
      func.apply(this, args)
    }
  }
}

6,防抖

// func是用戶傳入需要防抖的函數(shù)
// wait是等待時(shí)間
const debounce = (func, wait = 50) => {
  // 緩存一個(gè)定時(shí)器id
  let timer = 0
  // 這里返回的函數(shù)是每次用戶實(shí)際調(diào)用的防抖函數(shù)
  // 如果已經(jīng)設(shè)定過定時(shí)器了就清空上一次的定時(shí)器
  // 開始一個(gè)新的定時(shí)器,延遲執(zhí)行用戶傳入的方法
  return function(...args) {
    if (timer) clearTimeout(timer)
    timer = setTimeout(() => {
      func.apply(this, args)
    }, wait)
  }
}

if([]==false){console.log(1)}; //true
if({}==false){console.log(2)};
if([]){console.log(3)} //true
if([1]==[1]){console.log(4)}
var foo={};
    var F = function(){};

    Object.prototype.a = 'a';
    Function.prototype.b='b';

    console.log(foo.a) ==>a;
    console.log(foo.b) ==>undefined;
    console.log(F.a)==>a;
    console.log(F.b)==>b;

7,作用域

    var num1 = 55;
    var num2 = 66;

    function f1(num,num1) {
        num = 100;
        num1 = 100;
        num2 = 100;
        console.log(num);
        console.log(num1);
        console.log(num2);
    }

    f1(num1, num2);
    console.log(num);
    console.log(num1);
    console.log(num2);

8,值類型和引用類型的傳遞


    function Person(name,age,salary) {
        this.name = name;
        this.age = age;
        this.salary = salary;
    }

    function f2(person) {
        person.name = 'ls';
        person = new Person('aa',18,10000);
    }

    var p = new Person('zs',18,20000);
    console.log(p.name);
    f2(p);
    console.log(p.name)

9,原型面試題


function F(){};
    Object.prototype.a=function(){
        console.log('a')
    }
    Function.prototype.b=function(){
        console.log('b')
    }

    var f= new F();
    f.a();
    f.b();
    F.a();
    F.b();
function A(){}
    A.prototype.n=1;

    var b = new A();
    A.prototype={
        n:2,
        m:3
    }

    var c= new A();
    console.log(b.n,b.m,c.n,c.m)
    

10,MVVM

//  觀察者 (發(fā)布訂閱)  觀察者  被觀察者
class Dep {
    constructor(){
        this.subs = []; // 存放所有的watcher
    }
    // 訂閱
    addSub(watcher){ // 添加 watcher
        this.subs.push(watcher);
    }
    // 發(fā)布
    notify(){
        this.subs.forEach(watcher=>watcher.update());
    }
}
// new Watcher
class Watcher{
    constructor(vm,expr,cb){
        this.vm = vm;
        this.expr = expr;
        this.cb = cb;
        // 默認(rèn)先存放一個(gè)老值
        this.oldValue = this.get();
    }
    get(){ // vm.$data.school  vm.$data.school.name
        Dep.target = this; // 先把自己放在this上
        // 取值 把這個(gè)觀察者 和數(shù)據(jù)關(guān)聯(lián)起來
        let value = CompileUtil.getVal(this.vm,this.expr);
        Dep.target = null; // 不取消 任何值取值 都會添加watcher
        return value;
    }
    update(){ // 更新操作 數(shù)據(jù)變化后 會調(diào)用觀察者的update方法
        let newVal = CompileUtil.getVal(this.vm,this.expr);
        if(newVal !== this.oldValue){
            this.cb(newVal);
        }   
    }
}
// vm.$watch(vm,'school.name',(newVal)=>{

// })


class Observer{ // 實(shí)現(xiàn)數(shù)據(jù)劫持功能
    constructor(data){
        this.observer(data);
    }
    observer(data){
        // 如果是對象才觀察
        if(data && typeof data == 'object'){
            // 如果是對象
            for(let key in data){
                this.defineReactive(data,key,data[key]);
            }
        }
    }
    defineReactive(obj,key,value){
        this.observer(value); // school :[watcher,watcher]   b:[watcher]
        let dep = new Dep() // 給每一個(gè)屬性 都加上一個(gè)具有發(fā)布訂閱的功能
        Object.defineProperty(obj,key,{
            get(){
                // 創(chuàng)建watcher時(shí) 會取到對應(yīng)的內(nèi)容,并且把watcher放到了全局上
                Dep.target && dep.addSub(Dep.target);
                return value;
            },
            set:(newVal)=>{ // {school:{name:'珠峰'}} school ={}
                if(newVal != value){
                    this.observer(newVal);
                    value = newVal;
                    dep.notify();
                }
            }
        });
    }
}
// 基類 調(diào)度
class Compiler{
    constructor(el,vm){
        // 判斷el屬性 是不是一個(gè)元素 如果不是元素 那就獲取他
        this.el = this.isElementNode(el)?el:document.querySelector(el);
        // 把當(dāng)前節(jié)點(diǎn)中的元素 獲取到 放到內(nèi)存中
        this.vm = vm;
        let fragment = this.node2fragment(this.el);
        // 把節(jié)點(diǎn)中的內(nèi)容進(jìn)行替換
        // 編譯模版 用數(shù)據(jù)編譯
        this.compile(fragment);
        // 把內(nèi)容在塞到頁面中

        this.el.appendChild(fragment);
    }
    isDirective(attrName){
        return attrName.startsWith('v-');
    }
    // 編譯元素的
    compileElement(node){
        let attributes = node.attributes; // 類數(shù)組
        [...attributes].forEach(attr=>{ // type="text" v-model="school.name"
            let {name,value:expr} = attr; // v-model="school.name"
            // 判斷是不是指令 //v-
            if(this.isDirective(name)){ // v-model v-html v-bind
                let [,directive]  = name.split('-');  // v-on:click
                let [directiveName,eventName] =directive.split(':');
                // 需要調(diào)用不同的指令來處理
                CompileUtil[directiveName](node,expr,this.vm,eventName);
            }
        })
    }
    // 編譯文本的
    compileText(node){ // 判斷當(dāng)前文本節(jié)點(diǎn)中內(nèi)容是否包含 {{xxx}} {{aaa}}
       let content = node.textContent;
       if(/\{\{(.+?)\}\}/.test(content)){
           // 文本節(jié)點(diǎn)
           CompileUtil['text'](node,content,this.vm); // {{a}} {}
       }
    }
    // 核心的編譯方法
    compile(node){ // 用來編譯內(nèi)存中的dom節(jié)點(diǎn)
        let childNodes = node.childNodes;
        [...childNodes].forEach(child=>{
            if(this.isElementNode(child)){
               this.compileElement(child);
               // 如果是元素的話 需要把自己傳進(jìn)去 在去遍歷子節(jié)點(diǎn)
               this.compile(child);
            }else{
                this.compileText(child);
            }
        });
    }
    // 把節(jié)點(diǎn)移動到內(nèi)存中
    node2fragment(node){
        // 創(chuàng)建一個(gè)文檔碎片
        let fragment = document.createDocumentFragment();
        let firstChild;
        while(firstChild = node.firstChild){
            // appendChild具有移動性
            fragment.appendChild(firstChild);
        }
        return fragment
    }
    isElementNode(node){ // 是不是元素節(jié)點(diǎn)
        return node.nodeType === 1; 
    }
}
// 3月16日開班
CompileUtil = {
    // 根據(jù)表達(dá)式取到對應(yīng)的數(shù)據(jù)
    getVal(vm,expr){ // vm.$data   'school'  [school,name]
        return expr.split('.').reduce((data,current)=>{
            return data[current];
        },vm.$data);
    },
    setValue(vm,expr,value){ //vm.$data 'school.name'  =  姜文
        expr.split('.').reduce((data,current,index,arr)=>{
            if(index == arr.length-1){
                return data[current] = value;
            }
            return data[current];
        },vm.$data);
    },
    // 解析v-model這個(gè)指令
    model(node,expr,vm){ // node是節(jié)點(diǎn) expr 是表達(dá)式 vm是當(dāng)前實(shí)例  school.name vm.$data
        // 給輸入框賦予value屬性  node.value = xxx
        let fn = this.updater['modelUpdater'];
        new Watcher(vm,expr,(newVal)=>{ // 給輸入框加一個(gè)觀察者 如果稍后數(shù)據(jù)更新了會觸發(fā)此方法,會拿新值 給輸入框賦予值
            fn(node,newVal);
        });
        node.addEventListener('input',(e)=>{
            let value = e.target.value; // 獲取用戶輸入的內(nèi)容
            this.setValue(vm,expr,value);
        })
        let value  = this.getVal(vm,expr); // 珠峰
        fn(node,value);
    },
    html(node,expr,vm){ // v-html="message"
        let fn = this.updater['htmlUpdater'];
        new Watcher(vm,expr,(newVal)=>{ 
            console.log(newVal)
            fn(node,newVal);
        });
        let value  = this.getVal(vm,expr); // 珠峰
        fn(node,value);
    },
    getContentValue(vm,expr){
        // 遍歷表達(dá)式 將內(nèi)容 重新替換成一個(gè)完整的內(nèi)容 返還回去
        return expr.replace(/\{\{(.+?)\}\}/g,(...args)=>{
            return this.getVal(vm,args[1]);
        })
    },
    on(node,expr,vm,eventName){ // v-on:click="change"   expr
        node.addEventListener(eventName,(e)=>{
            vm[expr].call(vm,e); // this.change
        })
    },
    text(node,expr,vm){ // expr =>  珠峰 {} {{c}}  =>  a b
        let fn = this.updater['textUpdater'];
        let content = expr.replace(/\{\{(.+?)\}\}/g,(...args)=>{
            // 給表達(dá)式每{{}} 都加上觀察者
            new Watcher(vm,args[1],()=>{
                fn(node,this.getContentValue(vm,expr)); // 返回了一個(gè)全的字符串
            });
            return this.getVal(vm,args[1]);
        });
        fn(node,content);
    },  
    updater:{
        htmlUpdater(node,value){ // xss攻擊
            node.innerHTML = value;
        },
        // 把數(shù)據(jù)插入到節(jié)點(diǎn)中
        modelUpdater(node,value){
            node.value = value;
        },
        // 處理文本節(jié)點(diǎn)的
        textUpdater(node,value){
            node.textContent = value;
        }
    }
}
class Vue{
    constructor(options){
        // this.$el $data $options
        this.$el = options.el;
        this.$data = options.data;
        let computed = options.computed;
        let methods = options.methods;
        // 這個(gè)根元素 存在 編譯模板
        if(this.$el){
            // 把數(shù)據(jù) 全部轉(zhuǎn)化成用Object.defineProperty來定義
            new Observer(this.$data);
            // 把數(shù)據(jù)獲取操作 vm上的取值操作 都代理到 vm.$data
            // {{getNewName}} reduce  vm.$data.getNeName
            for(let key in computed){ // 有依賴關(guān)系 數(shù)據(jù)
                Object.defineProperty(this.$data,key,{
                    get:()=>{
                        return computed[key].call(this);
                    }
                })
            };

            for(let key in methods){
                Object.defineProperty(this,key,{
                    get(){
                        return methods[key]
                    }
                })
            }
            this.proxyVm(this.$data);


            new Compiler(this.$el,this);
        }
    }
    //backbone set()  get()
    proxyVm(data){
        for(let key in data){ // {school:{name,age}}
            Object.defineProperty(this,key,{ // 實(shí)現(xiàn)可以通過vm取到對應(yīng)的內(nèi)容
                get(){
                    return data[key]; // 進(jìn)行了轉(zhuǎn)化操作
                },
                set(newVal){ // 設(shè)置代理方法
                    data[key] = newVal;
                }
            })
        }
    }
}

11,深拷貝

function copy(obj) {
        var vv = obj instanceof Array ? []:{};

        for(var i in obj) {
            if(typeof obj[i] == 'object'){
                vv[i] = copy(obj[i])
            }else {
                vv[i] = obj[i]
            }
        }
        return vv
    }
//考慮null
function copy(obj) {

        var vv = null;
        if(typeof obj == 'object' && obj !== null) {
            vv = obj instanceof Array ? [] : {};

            for(var i in obj) {
                vv[i] = copy(obj[i])
            }
        }else {
            vv = obj
        }
        
        return vv
    }

12,函數(shù)調(diào)用

function Foo(){
    getName = function(){
        console.log(1)
    }
    return this;
}
Foo.getName = function(){
    console.log(2)
}
Foo.prototype.getName = function(){
    console.log(3)
}
var getName = function(){
    console.log(4)
}
function getName(){
    console.log(5)
}
// ouput:
Foo.getName();
getName();
Foo().getName();
getName();
new Foo.getName();
new Foo().getName();
new new Foo().getName();

13,觀察者模式

class Subject{
        constructor(){
            this.subs = [];
        }

        subscribe(sub) {
            this.subs.push(sub)
        }

        unsubscribe(sub) {
            const index = this.subs.indexOf(sub);
            if(index > -1){
                this.subs.splice(index,1);
            }
        }

        fire() {
            this.subs.forEach(sub=>{
                sub.notify()
            })
        }
    }

    class Observer {
        constructor(data){
            this.data
        }

        notify(){
            console.log(this.data)
        }
    }

    let subject = new Subject()
    let ob1 = new Observer('hello')
    let ob2 = new Observer('world')
    subject.subscribe(ob1)
    subject.subscribe(ob2)
    subject.fire()

14,發(fā)布訂閱模式

class EventChannel {
  constructor() {
    // 主題
    this.subjects = {}
  }

  hasSubject(subject) {
    return this.subjects[subject] ? true : false
  }

  /**
   * 訂閱的主題
   * @param {String} subject 主題
   * @param {Function} callback 訂閱者
   */
  on(subject, callback) {
    if (!this.hasSubject(subject)) {
      this.subjects[subject] = []
    }
    this.subjects[subject].push(callback)
  }

  /**
   * 取消訂閱
   */
  off(subject, callback) {
    if (!this.hasSubject(subject)) {
      return
    }
    const callbackList = this.subjects[subject]
    const index = callbackList.indexOf(callback)
    if (index > -1) {
      callbackList.splice(index, 1)
    }
  }

  /**
   * 發(fā)布主題
   * @param {String} subject 主題
   * @param {Argument} data 參數(shù)
   */
  emit(subject, ...data) {
    if (!this.hasSubject(subject)) {
      return
    }
    this.subjects[subject].forEach(callback => {
      callback(...data)
    })
  }
}

const channel = new EventChannel()

channel.on('update', function(data) {
  console.log(`update value: ${data}`)
})

channel.emit('update', 123)

15,柯里化函數(shù)

柯里化的定義:在數(shù)學(xué)和計(jì)算機(jī)科學(xué)中,柯里化是一種將使用多個(gè)參數(shù)的一個(gè)函數(shù)轉(zhuǎn)換成一系列使用一個(gè)參數(shù)的函數(shù)的技術(shù)。

function curry (fn, currArgs) {
    return function() {
        let args = [].slice.call(arguments);

        // 首次調(diào)用時(shí),若未提供最后一個(gè)參數(shù)currArgs,則不用進(jìn)行args的拼接
        if (currArgs !== undefined) {
            args = args.concat(currArgs);
        }

        // 遞歸調(diào)用
        if (args.length < fn.length) {
            return curry(fn, args);
        }

        // 遞歸出口
        return fn.apply(null, args);
    }
}

測試一下
柯里化的目的是,減少代碼冗余,以及增加代碼的可讀性

function sum(a, b, c) {
    console.log(a + b + c);
}

const fn = curry(sum);

fn(1, 2, 3); // 6
fn(1, 2)(3); // 6
fn(1)(2, 3); // 6
fn(1)(2)(3); // 6
都能輸出 6 了,搞定!
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 第3章 基本概念 3.1 語法 3.2 關(guān)鍵字和保留字 3.3 變量 3.4 數(shù)據(jù)類型 5種簡單數(shù)據(jù)類型:Unde...
    RickCole閱讀 5,540評論 0 21
  • 函數(shù)和對象 1、函數(shù) 1.1 函數(shù)概述 函數(shù)對于任何一門語言來說都是核心的概念。通過函數(shù)可以封裝任意多條語句,而且...
    道無虛閱讀 4,963評論 0 5
  • Swift1> Swift和OC的區(qū)別1.1> Swift沒有地址/指針的概念1.2> 泛型1.3> 類型嚴(yán)謹(jǐn) 對...
    cosWriter閱讀 11,684評論 1 32
  • JavaScript語言精粹 前言 約定:=> 表示參考相關(guān)文章或書籍; JS是JavaScript的縮寫。 本書...
    微笑的AK47閱讀 663評論 0 3
  • (稻盛哲學(xué)學(xué)習(xí)會)打卡第22天 姓名:黃文兵 部門:技術(shù)部 組別:待定 【知~學(xué)習(xí)】 誦讀《活法》第二章 . 從原...
    JackHWB閱讀 273評論 0 0

友情鏈接更多精彩內(nèi)容