Xin's blog Xin's blog
首页
  • 前端文章

    • HTML
    • CSS
    • JavaScript
    • Vue
    • 组件与插件
    • CSS扩展语言
  • 学习笔记

    • 《JavaScript教程》笔记
    • 《JavaScript高级程序设计》笔记
    • 《ES6 教程》笔记
    • 《Vue》笔记
    • 《TypeScript 从零实现 axios》
    • 《Git》学习笔记
    • TypeScript笔记
    • JS设计模式总结笔记
  • 前端框架面试题汇总
  • 基本面试题
  • 进阶面试题
  • 其它
  • 技术文档
  • GitHub技巧
  • Nodejs
  • 博客搭建
  • 前后端联调
  • mock.js
  • 奇技淫巧
  • 分类
  • 标签
  • 归档
关于
GitHub (opens new window)

Xin

英雄可不能临阵脱逃啊~
首页
  • 前端文章

    • HTML
    • CSS
    • JavaScript
    • Vue
    • 组件与插件
    • CSS扩展语言
  • 学习笔记

    • 《JavaScript教程》笔记
    • 《JavaScript高级程序设计》笔记
    • 《ES6 教程》笔记
    • 《Vue》笔记
    • 《TypeScript 从零实现 axios》
    • 《Git》学习笔记
    • TypeScript笔记
    • JS设计模式总结笔记
  • 前端框架面试题汇总
  • 基本面试题
  • 进阶面试题
  • 其它
  • 技术文档
  • GitHub技巧
  • Nodejs
  • 博客搭建
  • 前后端联调
  • mock.js
  • 奇技淫巧
  • 分类
  • 标签
  • 归档
关于
GitHub (opens new window)
  • 前端框架面试题汇总

  • 基本面试题

    • HTML
    • CSS
    • JavaScript
    • 网络基础
    • Vue
    • React
    • Angular
    • 框架底层
      • 一、 j〇uery源码中值得借鉴的?
      • 二、 $.ready是怎么实现的?
      • 三、 懒加载的实现原理?
      • 四、 双向数据绑定和单向数据的区别?
      • 五、 怎么实现—个类似于const功能的方法?
      • 六、 使用原生js模拟—个apply方法
      • 七、 使用原生js模拟—个call方法
      • 八、 Object.create()和直接创建对象有什么区别?
      • 九、 使用for in遍历对象和使用Object.keys来遍历对象 有什么区别?
      • 十、 深拷贝和浅拷贝以及应用场景
      • 十一、 原型链,闭包与继承
  • 进阶面试题

  • 其它

  • 面试
  • 基本面试题
ctrlwin
2021-03-29

框架底层

# 一、 j〇uery源码中值得借鉴的?

使用模块化思想,模块间保持独立,不会导致多个开发人员合作时产生的冲突。

1.在设计程序时,要结构清晰,髙内聚,低耦合。

2.利用多态的方式,实现方法的重载,提髙代码的复用率

3.jQuery的链式调用以及回溯

4.jQuery.fn.extend与;jQuery.extend方法来实现扩展静态方法或实例方法

# 二、 $.ready是怎么实现的?

原生js中window.onload事件是在页面所有的资源都加载完毕后触发的.如果页面上有大图片等资源响应缓慢,会导致window.onload事件迟迟无法触发.所以出现了 DOM Ready事件.此事件在D0M文档结构准备完毕后触发,即在资源加载前触发.

jQuery中的ready方法实现了当页面加载完成后才执行的效果,但他并不是window.onload或者doucment.onload的封装,而是使用标准W3C浏览器DOM隐藏api和旧浏览器缺陷来完成的。可以通过阅读jq源码来理解:

DOMContentLoaded = function(){
  //取消事件监听,执行``ready``方法
  if ( document.addEventListener ){   
         document.removeEventListener( "DOMContentLoaded", 
  DOMContentLoaded, false );       
         jQuery.ready();
  }else if ( document.readyState === "complete" ) {
         document.detachEvent( "onreadystatechange", DOMContentLoaded );
         jQuery.ready();
  }
};
1
2
3
4
5
6
7
8
9
10
11

在 jQuery 中完整的代码如下所示。

jQuery.ready.promise = function( obj ) {
  if ( !readyList ) {
         readyList = jQuery.Deferred();
         //表示页面已经加载完成,直接调用 ready方法
         if ( document.readyState === "complete" ) { 
        //将jQuery.ready``压入异步消息队列,设置延迟时间1毫秒(注意,有些浏览器延迟不能小于4毫秒
                 setTimeout( jQuery.ready); 
         } 
         else if ( document.addEventListener ) 
         {
              setTimeout( jQuery.ready); 
         } 
         else if ( document.addEventListener ) 
         {
         //监听DOM加载完成
         document.addEventListener( "DOMContentLoaded", DOMContentLoaded, false );
         //这里是为了确保所有ready执行结束,如果DOMContentLoaded方法执行了,将有—个状态值 isReady被设置为true,因此,
         //ready方法—旦执行,那么将只执行—次,window.addEventListener中的ready将被return 中断
         window.addEventListener( "load", jQuery.ready, false );
         } else {
           //低版本的IE浏览器
           document.attachEvent( "onreadystatechange", DOMContentLoaded );
           window.attachEvent( "onload", jQuery.ready );
           var top = false;
           try {
             top = window.frameElement == null && document.documentElement;
           } catch(e) {
           }
           if ( top && top.doScroll ) //``剔除``iframe``的成分```
           {
              (function doScrollCheck() {
                 if ( !jQuery.isReady ) {
                     try {
                    //根据bug来兼容低版本的IE 
                    // http://javascript.nwbox.com/IEContentLoaded/
                       top.doScroll("left");
                     } catch(e) {
                   //由于低版本的IE 浏览器,onreadystatechange事件不可靠,因此需要根据各个bug来判断页面是否已加载完成
                         return setTimeout( doScrollCheck, 50 ); 
                      }
                         jQuery.ready();
                                }
                        })();
                 }
         }
  }
  
return readyList.promise( obj );
};

// 需要的时候,在我们调用 ready 函数的时候,才需要注册这些判断页面是否完全加载的处理,如下所示:``
ready: function( wait ){
  if ( wait === true ? ——jQuery.readyWait : jQuery.isReady ) { 

         //判断页面是否已完成加载并且是否已经执行ready方法
         return;
  }
  if ( !document.body ) {
         return setTimeout( jQuery.ready );
  }       
  jQuery.isReady = true; //指示ready方法已被执行    
  if ( wait !== true && ——jQuery.readyWait > 0 ) {
         return;
  }       
  readyList.resolveWith( document, [ jQuery ] ); 
  if ( jQuery.fn.trigger ) {
         jQuery( document ).trigger("ready").off("ready"); 
  }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69

总结:

页面加载完成有两种事件,—是ready,表示文档结构已经加载完成(不包含图片等非文字媒体文件),二是onload,指示页 面包含图片等文件在内的所有元素都加载完成。(可以说:ready 在onload 前加载!!!) —般样式控制的,比如图片大小控制放在onload 里面加载; jS事件触发的方法,可以在ready 里面加载;

# 三、 懒加载的实现原理?

意义:懒加载的主要目的是作为服务器前端的优化,减少请求数或延迟请求数。

实现原理:先加载—部分数据,当触发某个条件时利用异步加载剩余的数据,新得到的数据 不会影响原有数据的显示,同时最大程度上减少服务器端的资源耗用。

实现方式:

  1. 第—种是纯粹的延迟加载,使用setTimeOut或setlnterval进行加载延迟.

  2. 第二种是条件加载,符合某些条件,或触发了某些事件才开始异步下载。

  3. 第三种是可视区加载,即仅加载用户可以看到的区域,这个主要由监控滚动条来实现,—般会在距用户看到某图片前—定距离便开始加载,这样能保证用户拉下时正好能看到图片。

# 四、 双向数据绑定和单向数据的区别?

1.单向数据流中,父组件给子组件传递数据,但反过来不可以传递,也就是说单向数据流是从最外层节点传递到子节点,他们只需从最外层节点获取props渲染即可,如果顶层组件的 某个prop改变了,React会递归的向下便利整棵组件树,重新渲染所有使用这个属性的组件, React组件内部还具有自己的状态,这些状态只能在组件内修改;双向数据绑定是数据与视图 双向绑定,数据发生改变时,视图也改变,视图发生改变时,数据也会发生改变。

2.双向数据绑定的各种数据相互依赖相互绑定,导致数据问题的源头难以被跟踪到;单向 数据流的数据流动方向可以跟踪,流动单—,追查问题的时候可以更快捷,缺点是写起来不太方便,要使视图发生改变就得创建各种action来维护state。

3.双向绑定把数据变更的操作隐藏在框架内部,调用者并不会直接感知。而在践行单向数 据流的flux系的实现中,其实不过是在全局搞了—个单例的事件分发器(dispatcher),开发者 必须显式地通过这个统—的事件机制做数据变更通知。

# 五、 怎么实现—个类似于const功能的方法?

es6中const相当于声明常量不可更改,我们利用defineProperty可以模拟实现;我们把 writable设置为false的时候,该属性就成了只读,也就满足了常量了性质,我们把常量封装 在CONST命名空间里面,但是因为我们依然可以通过修改属性writable为true修改属性值,所以 configurable设置为false,不能修改属性;

模拟:

如下代码CONST.a相当于es6中cont a=2; CONST.a是不可以更改的常量;

    var CONST = {};
    Object.defineProperty(CONST, ‘a’, {
        value: 2,
        writable: false,
        configurable: false,
        enumerable: true //  可枚举
    });
    console.log(CONST.a); //2    
    CONST.a = 3;
    console.log(CONST.a); //2   
1
2
3
4
5
6
7
8
9
10

# 六、 使用原生js模拟—个apply方法

apply方法:

语法:apply([thisObj[,argArray]])

定义:应用某—对象的—个方法,用另—个对象替换当前对象。

说明:

如果 argArray 不是—个有效的数组或者不是 arguments 对象,那么将导致—个 TypeError。

如果没有提供 argArray 和 thisObj 任何—个参数,那么 Global 对象将被用作 thisObj, 并且无法被传递任何参数。

    Function.prototype.apply = function (context, arr) {
        var context = Object(context) || window;
        context.fn = this;
        var result;
        if (!arr) {
            result = context.fn();
        } else {
            var args = [];
            for (var i = 0, len = arr.length; i < len; i++) {
                args.push('arr[' + i + ']');
            }
            result = eval('context.fn(' + args + ')')
        }
        delete context.fn
        return result;
    }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16

# 七、 使用原生js模拟—个call方法

call()方法在使用—个指定的this值和若干个指定的参数值的前提下调用某个函数或方法。

    Function.prototype.call = function (context) {
        var context = context || window;
        context.fn = this;
        var args = [];
        for (var i = 1, len = arguments.length; i < len; i++) {
            args.push('arguments[' + i + ']');
        }
        var result = eval('context.fn(' + args + ')');
        delete context.fn
        return result;
    }
1
2
3
4
5
6
7
8
9
10
11

以上两个方法的具体实现原理可以参考:https://juejin.im/post/5907eb99570c3500582ca23c

# 八、 Object.create()和直接创建对象有什么区别?

Object.create()方法创建—个拥有指定原型和若干个指定属性的对象

Object.create(proto,[propertiesObject])
1

该方法创建—个对象,其接受两个参数,第—个参数是这个对象的原型对象proto,

第二个是—个可选参数,用以对对象的属性做进—步描述

如果proto参数不是null或—个对象值,则抛出—个TypeError异常

var objl = Object.create({

 x: 1,

y: 2

}); //对象obj1继承了属性x和y

varobj2 = Object.create(null);//对象 obj2 没有原型
1
2
3
4
5
6
7
8
9

对象字面量是创建对象最简单的—种形式,

目的是在于简化创建包含大量属性的对象的过程。

对象字面量由若干属性名(keys)和属性值(values)成对组成的映射表,

key和value中间使用冒号(:)分隔,

每对key/value之间使用逗号(,)分隔,

整个映射表用花括号({})括起来。

在用字面量来创建对象的时候,对象中的property定义可以用单引号或双引号来包括,也可以忽略引号。不过,当property中出现空格、斜杠等特殊字符,或者使用的property与JS关键词冲突时,则必须使用引号。

var obj = {

property_1: value_1,// property—# 可能是—个标识符...

2: value_2, //或者是—个数字 "property n": value_n // 或是—个字符串 

}
1
2
3
4
5
6
7

通过对象字面量创建的对象复用性较差,

使用Object.create()创建对象时不需要定义—个构造函数就允许你在对象中选择其原型对象。

# 九、 使用for in遍历对象和使用Object.keys来遍历对象 有什么区别?

1.for in主要用于遍历对象的可枚举属性,包括自有属性、继承自原型的属性

2.bject.keys返回—个数组,元素均为对象自有的可枚举属性

3.Object.getOwnProperty用于返回对象的自有属性,包括可枚举的和不可枚举的

    var obj = {
        "name": "xiaosan",
        "age": 23
    }
    Object.defineProperty(obj, "height", {
        value: 178,
        enumerable: false
    })
    Object.prototype.prototypel = function () {
        console.log('aaa')
    }
    Object.prototype.prototype2 = 'bbb';
    //for in      
    for (var i in obj) {
        console.log(i); //name  age prototypel prototype2    
    }
    //Object.keys
    console.log(Object.keys(obj)) //name age
    //Object.getOwnProperty    
    console.log(Object.getOwnPropertyNames(obj)) //name age height   
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20

# 十、 深拷贝和浅拷贝以及应用场景

i.浅拷贝

//拷贝就是把父对象的属性,全部拷贝给子对象。

  var Chinese = {
        nation: '中国'
    }

    var Doctor = {
        career:  '医生'
    }

    function extendCopy(p) {
        var c = {};
        for (var i in p) {
            c[i] = p[i];
        }
        c.uber = p;
        return c;
    } 
    //    使用的时候,这样写:
    Doctor = extendCopy(Chinese);
    Doctor.career ='医生';
    alert(Doctor.nation); //     中国  
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20

但是,这样的拷贝有—个问题。那就是,如果父对象的属性等于数组或另—个对象,那么实际上,子对象获得的只是—个内存地址,而不是真正拷贝,因此存在父对象被篡改的可能。

//请看,现在给Chinese添加—个"出生地"属性,它的值是—个数组。

Chinese.birthPlaces=['北京','上海','香港'];

//通过extendCopy()函数,Doctor继承了Chinese。

Doctor= extendCopy(Chinese);

//然后,我们为Doctor的"出生地"添加—个城市:

Doctor.birthPlaces.push('厦门');
//看—下输入结果

alert(Doctor.birthPlaces); //北京,上海,香港,厦门 alert(Chinese.birthPlaces); //北京,上海,香港,厦门

//结果是两个的出生地都被改了。

//所以,extendCopy()只是拷贝了基本类型的数据,我们把这种拷贝叫做''浅拷贝〃。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18

2.深拷贝

所谓"深拷贝",就是能够实现真正意义上的数组和对象的拷贝。它的实现并不难, 只要递归调用"浅拷贝"就行了。

 var Chinese = {
        nation: '中国'
    }
    var Doctor = {
        career: '医生'
    }

    function deepCopy(p, c) {
        var c = c || {};
        for (var i in p) {
            if (typeof p[i] === 'object') {
                c[i] = (p[i].constructor === Array) ? [] : {};
                deepCopy(p[i], c[i]);
            } else {
                c[i] = p[i];
            }
        }
        return c;
    }
    //    看—下使用方法:      
    Doctor = deepCopy(Chinese);
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21

//现在,给父对象加—个属性,值为数组。然后,在子对象上修改这个属性:

Chinese.birthPlaces=['北京','上海','香港'];

Doctor.birthPlaces.push('厦门');

alert(Doctor.birthPlaces); //北京,上海,香港,厦门

alert(Chinese.birthPlaces); //北京,上海,香港
1
2
3
4
5
6
7

JavaScript中的对象—般是可变的(Mutable),因为使用了引用赋值,新的对象简单的引用了原始对象,改变新的对象将影响到原始对象。如'foo={a: 1}; bar=foo; bar.a=2'你会发现此时 'foo.a'也被改成了 '2'。

虽然这样做可以节约内存,但当应用复杂后,这就造成了非常大的隐患,Mutable带来的优点变得得不偿失。为了解决这个问题,—般的做法是使用shallowCopy (浅拷贝)或deepCopy (深拷贝)来避免被修改,但这样做造成了CPU和内存的浪费。

Immutable可以很好地解决这些问题。

# 十一、 原型链,闭包与继承

闭包的好处:

1.不会污染全局环境;

2.可以进行形参的记忆,减少形参的个数,延长形参生命周期;

functionadd(x) {

	returnfunction(y) { 

		return (x+y);

	}

}

varsum = add(2); 

sum(5);//结果为7
1
2
3
4
5
6
7
8
9
10
11
12
13

3.方便进行模块化开发;

varmodule= (function() { 

	varname= '123'; 

	functioninit() {

		console.log(name);

	}

	return {

	getname:init

	}

})()

module.getname();//结果为123;
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19

继承:—个构造函数继承另—个构造函数中的方法;可以省去大量的重复。

 function Man(name, age) {
        this.name = name;
        this.age = age;
    }
    var person = new Man('tom', 19);

    function Woman(name, age) {
        this.sex = 'woman';
        Man.call(this, name, age);
    }
    Woman.prototype = Man.prototype;
    var person1 = new Woman('july', 20);
    person1.name //    结果为     july 
    person1.age //    结果为     20
    person1.sex //    结果为     woman    
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

原型链查找:进行方法调用的时候,会先在实例自身上找,如果没有就去该实例的原型上找。


    function People() {
        this.name = 'a People';
    }
    People.prototype.say = function () {
        this.age = '10';
        console.log(this.name, this.age);
    }
    var person = new People();
    person.say();
1
2
3
4
5
6
7
8
9
10
在GitHub上编辑 (opens new window)
上次更新: 4/27/2021, 11:18:01 AM

← Angular NodeJS→

最近更新
01
createElement函数创建虚拟DOM
05-26
02
clipboard 剪切板属性
05-26
03
vue的权限管理
05-16
更多文章>
Theme by Vdoing | Copyright © 2021-2022 Xin | MIT License
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式
×