前端面试题知识点(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区别

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,节流

// func是用户传入需要防抖的函数
// wait是等待时间
const throttle = (func, wait = 50) => {
  // 上一次执行该函数的时间
  let lastTime = 0
  return function(...args) {
    // 当前时间
    let now = +new Date()
    // 将当前时间和上一次执行函数时间对比
    // 如果差值大于设置的等待时间就执行函数
    if (now - lastTime > wait) {
      lastTime = now
      func.apply(this, args)
    }
  }
}

6,防抖

// func是用户传入需要防抖的函数
// wait是等待时间
const debounce = (func, wait = 50) => {
  // 缓存一个定时器id
  let timer = 0
  // 这里返回的函数是每次用户实际调用的防抖函数
  // 如果已经设定过定时器了就清空上一次的定时器
  // 开始一个新的定时器,延迟执行用户传入的方法
  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

//  观察者 (发布订阅)  观察者  被观察者
class Dep {
    constructor(){
        this.subs = []; // 存放所有的watcher
    }
    // 订阅
    addSub(watcher){ // 添加 watcher
        this.subs.push(watcher);
    }
    // 发布
    notify(){
        this.subs.forEach(watcher=>watcher.update());
    }
}
// new Watcher
class Watcher{
    constructor(vm,expr,cb){
        this.vm = vm;
        this.expr = expr;
        this.cb = cb;
        // 默认先存放一个老值
        this.oldValue = this.get();
    }
    get(){ // vm.$data.school  vm.$data.school.name
        Dep.target = this; // 先把自己放在this上
        // 取值 把这个观察者 和数据关联起来
        let value = CompileUtil.getVal(this.vm,this.expr);
        Dep.target = null; // 不取消 任何值取值 都会添加watcher
        return value;
    }
    update(){ // 更新操作 数据变化后 会调用观察者的update方法
        let newVal = CompileUtil.getVal(this.vm,this.expr);
        if(newVal !== this.oldValue){
            this.cb(newVal);
        }   
    }
}
// vm.$watch(vm,'school.name',(newVal)=>{

// })


class Observer{ // 实现数据劫持功能
    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() // 给每一个属性 都加上一个具有发布订阅的功能
        Object.defineProperty(obj,key,{
            get(){
                // 创建watcher时 会取到对应的内容,并且把watcher放到了全局上
                Dep.target && dep.addSub(Dep.target);
                return value;
            },
            set:(newVal)=>{ // {school:{name:'珠峰'}} school ={}
                if(newVal != value){
                    this.observer(newVal);
                    value = newVal;
                    dep.notify();
                }
            }
        });
    }
}
// 基类 调度
class Compiler{
    constructor(el,vm){
        // 判断el属性 是不是一个元素 如果不是元素 那就获取他
        this.el = this.isElementNode(el)?el:document.querySelector(el);
        // 把当前节点中的元素 获取到 放到内存中
        this.vm = vm;
        let fragment = this.node2fragment(this.el);
        // 把节点中的内容进行替换
        // 编译模版 用数据编译
        this.compile(fragment);
        // 把内容在塞到页面中

        this.el.appendChild(fragment);
    }
    isDirective(attrName){
        return attrName.startsWith('v-');
    }
    // 编译元素的
    compileElement(node){
        let attributes = node.attributes; // 类数组
        [...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(':');
                // 需要调用不同的指令来处理
                CompileUtil[directiveName](node,expr,this.vm,eventName);
            }
        })
    }
    // 编译文本的
    compileText(node){ // 判断当前文本节点中内容是否包含 {{xxx}} {{aaa}}
       let content = node.textContent;
       if(/\{\{(.+?)\}\}/.test(content)){
           // 文本节点
           CompileUtil['text'](node,content,this.vm); // {{a}} {{b}}
       }
    }
    // 核心的编译方法
    compile(node){ // 用来编译内存中的dom节点
        let childNodes = node.childNodes;
        [...childNodes].forEach(child=>{
            if(this.isElementNode(child)){
               this.compileElement(child);
               // 如果是元素的话 需要把自己传进去 在去遍历子节点
               this.compile(child);
            }else{
                this.compileText(child);
            }
        });
    }
    // 把节点移动到内存中
    node2fragment(node){
        // 创建一个文档碎片
        let fragment = document.createDocumentFragment();
        let firstChild;
        while(firstChild = node.firstChild){
            // appendChild具有移动性
            fragment.appendChild(firstChild);
        }
        return fragment
    }
    isElementNode(node){ // 是不是元素节点
        return node.nodeType === 1; 
    }
}
// 3月16日开班
CompileUtil = {
    // 根据表达式取到对应的数据
    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这个指令
    model(node,expr,vm){ // node是节点 expr 是表达式 vm是当前实例  school.name vm.$data
        // 给输入框赋予value属性  node.value = xxx
        let fn = this.updater['modelUpdater'];
        new Watcher(vm,expr,(newVal)=>{ // 给输入框加一个观察者 如果稍后数据更新了会触发此方法,会拿新值 给输入框赋予值
            fn(node,newVal);
        });
        node.addEventListener('input',(e)=>{
            let value = e.target.value; // 获取用户输入的内容
            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){
        // 遍历表达式 将内容 重新替换成一个完整的内容 返还回去
        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 =>  珠峰 {{b}} {{c}}  =>  a b
        let fn = this.updater['textUpdater'];
        let content = expr.replace(/\{\{(.+?)\}\}/g,(...args)=>{
            // 给表达式每{{}} 都加上观察者
            new Watcher(vm,args[1],()=>{
                fn(node,this.getContentValue(vm,expr)); // 返回了一个全的字符串
            });
            return this.getVal(vm,args[1]);
        });
        fn(node,content);
    },  
    updater:{
        htmlUpdater(node,value){ // xss攻击
            node.innerHTML = value;
        },
        // 把数据插入到节点中
        modelUpdater(node,value){
            node.value = value;
        },
        // 处理文本节点的
        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;
        // 这个根元素 存在 编译模板
        if(this.$el){
            // 把数据 全部转化成用Object.defineProperty来定义
            new Observer(this.$data);
            // 把数据获取操作 vm上的取值操作 都代理到 vm.$data
            // {{getNewName}} reduce  vm.$data.getNeName
            for(let key in computed){ // 有依赖关系 数据
                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,{ // 实现可以通过vm取到对应的内容
                get(){
                    return data[key]; // 进行了转化操作
                },
                set(newVal){ // 设置代理方法
                    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,函数调用

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,发布订阅模式

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)
    }
  }

  /**
   * 发布主题
   * @param {String} subject 主题
   * @param {Argument} data 参数
   */
  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,柯里化函数

柯里化的定义:在数学和计算机科学中,柯里化是一种将使用多个参数的一个函数转换成一系列使用一个参数的函数的技术。

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

        // 首次调用时,若未提供最后一个参数currArgs,则不用进行args的拼接
        if (currArgs !== undefined) {
            args = args.concat(currArgs);
        }

        // 递归调用
        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 了,搞定!
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 206,839评论 6 482
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 88,543评论 2 382
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 153,116评论 0 344
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 55,371评论 1 279
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 64,384评论 5 374
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,111评论 1 285
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,416评论 3 400
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,053评论 0 259
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,558评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,007评论 2 325
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,117评论 1 334
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,756评论 4 324
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,324评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,315评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,539评论 1 262
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,578评论 2 355
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,877评论 2 345

推荐阅读更多精彩内容

  • 第3章 基本概念 3.1 语法 3.2 关键字和保留字 3.3 变量 3.4 数据类型 5种简单数据类型:Unde...
    RickCole阅读 5,099评论 0 21
  • 函数和对象 1、函数 1.1 函数概述 函数对于任何一门语言来说都是核心的概念。通过函数可以封装任意多条语句,而且...
    道无虚阅读 4,527评论 0 5
  • Swift1> Swift和OC的区别1.1> Swift没有地址/指针的概念1.2> 泛型1.3> 类型严谨 对...
    cosWriter阅读 11,089评论 1 32
  • JavaScript语言精粹 前言 约定:=> 表示参考相关文章或书籍; JS是JavaScript的缩写。 本书...
    微笑的AK47阅读 576评论 0 3
  • (稻盛哲学学习会)打卡第22天 姓名:黄文兵 部门:技术部 组别:待定 【知~学习】 诵读《活法》第二章 . 从原...
    JackHWB阅读 229评论 0 0