Published on

《js 忍者秘籍》摘记

Authors
  • avatar
    Name
    jacob jiang

相关连接:

  • sudheerj/javascript-interview-questions: List of 1000 JavaScript Interview Questions (github.com)

    js API 知识点

    1. 创建 对象方法

    //不推荐
    var object = new Object();
    
    //创建没有原型链
    var object = Object.create(null);
    
    //通过构造函数
    function Person(name){
       this.name=name;
       this.age=21;
    }
    Person.prototype.name = "Sudheer";
    var object = new Person("Sudheer");
    
    //通过字面量
    var object = {
         name: "Sudheer"
         age: 34
    };
    
    

    JavaScript / Object.create — DevDocs

    //构造函数的形式等价于
    // Create a new instance using function prototype.
    var newInstance = Object.create(func.prototype)
    
    // Call the function
    var result = func.call(newInstance, x, y, z),
    
    // If the result is a non-null object then use it otherwise just use the new instance.
    console.log(result && typeof result === 'object' ? result : newInstance);
    

    📝 手写new 函数

    export default function __new(Constructor,...args){
        if(!(typeof Constructor === 'function'&&!Constructor.prototype)){
            throw new TypeError(FUNC_ERROR_TEXT)
        }
        //创建对象,并且将原型指向构造函数的原型链
        /**
         * 等价于 let a = {}; a.prototype = Constructor.prototype
         */
        let instance = Object.create(Constructor.prototype);
        Constructor.call(instance,...args);
        return instance;
    }
    

    ES6 语法

    class Person {
       constructor(name) {
          this.name = name;
       }
       sayHi(){
       	 return "hello"
       }
    }
    
    var object = new Person("Sudheer");
    
  1. 什么是原型链

为什么需要?用来创建对象,基于存在的对象【复用】。类似于基于类的面向对象语言的类的作用。

怎么获得?对于对象通过 .__proto__ ,对于(函数/构造器)通过prototype 获取。

或者使用getPrototypeOf 方法(推荐)。设置使用setPrototypeOf

__proto__ prototype

__proto__是浏览器上环境prototype的实现(我也喜欢用它来区别对象与构造函数的原型)。

prototype 是真正意义上的原型。

内置对象本质上是个函数,函数本身是个对象,他也拥有原型对象并且该原型的构造器指向其本身

并且发现Object 只拥有__proto__ 而不是[[prototype]]。[Math 作为 Namespace 对象 也是一样的]

可以去观察其他内置对象(例如:Date,Number 的[[prototype]] 指向 Object 内置对象)

image-20211027111853146

直接通过字面量创建的原型对象,发现上面挂了许多方法

image-20211027105240739

注意:原型对象也是对象(拥有__proto__) 。而且该原型对象的__proto__为Object 的原型对象。并且Object原型对象的__proto__ 为null

使用构造函数创建的原型对象

image-20211027105134348

注意:以构造器的原型对象为桥梁【和构造函数的连接是双向的】,连接构造函数与实例对象

image-20211027110324293

类似下面中效果

image-20201108135550500

小总结:

  1. 每个函数都拥有一个原型对象[[prototype]] ,并且其constructor 的 指向 其本身。

    拥有constructor 的必定为原型对象。 反之不是。

  2. 使用构造函数创建对象,**等价于,**使用字母量创建对象之后 将 __proto__ 指向构造函数的原型。

    【因此该对象与构造函数共用一个原型对象】,至此根据原型链上查找属性规则:将会复用原型对象上的方法。

  3. Object 的原型对象__proto__ 为null 原型对象只能为Object或 null 。

  4. 绝大部分的内置对象的prototype 指向 double Object 的原型对象

面试题:如何实现继承?

export default function __extend(Parent){
    // 检测是否为构造函数
    if(!(typeof Parent === 'function'&&Parent.prototype.constructor == Parent)){
        throw new TypeError(NOT_CONSTRUCTOR_TEXT)
    }
    let __proto__ =  new Parent();
    // 赋值
    for(let key in this.prototype){
        __proto__[key] = this.prototype[key] 
    }
    //this 为子类,或者说为子构造函数
    this.prototype = __proto__;
    //构造函数的原型 constructor 必须指向 构造函数
    Object.defineProperty(this.prototype,"constructor",{
        enumerable:false,
        writable:true,
        value:this
    })
}

image-20220304155148482

面试题:如何实现 instanceOf

const NOT_CONSTRUCTOR_TEXT = 'Expected a constructor';
export default function __instanceOf(Parent){
    // 检测Parent是否为构造函数
    if(!(typeof Parent === 'function'&&Parent.prototype.constructor == Parent)){
        throw new TypeError(NOT_CONSTRUCTOR_TEXT)
    }
    let __proto__ = this.__proto__;
    //可能传入不是对象
    while(__proto__){
        if(__proto__ === Parent.prototype){
            return true;
        }
        __proto__ =  __proto__.__proto__;
    };
    return false;
}
  1. call,apply,bind 区别

相同点:都可以改变this 的指向。 三者第一个参数都为this 的指向,调用者为函数。

不同点:前两者都是执行函数。call 为参数列表,apply 为参数数组。

bind 返回为改变了this 的函数。 ​​ call bitch

  1. 什么是JSON,它的基本操作

    ①JavaScript object notation 是 MIME 类型之一,作为网络传输一种格式。

    JSON.parse(text) 将符合stringify 的字符串转为对象。

    JSON.stringify(value, replacer, space) 转为可运输的对象。

    1. 基本类型 string,number,boolean,null 正常转化

    undefined,Symbol,Function 在对象中转化为 直接忽略,在数组中转为 null。

    undefined when passing in "pure" values like JSON.stringify(function(){}) or JSON.stringify(undefined).

    1. All Symbol-keyed properties will be completely ignored, even when using the replacer function.
    2. The numbers Infinity and NaN, as well as the value null, are all considered null.

    // 删除value 为字符串 属性值
    function replacer(key, value) {
      // Filtering out properties
      if (typeof value === 'string') {
        return undefined; //删除该键值对
      }
      return value;
    }
    
    var foo = {foundation: 'Mozilla', model: 'box', week: 45, transport: 'car', month: 7};
    JSON.stringify(foo, replacer);
    // '{"week":45,"month":7}'
    

    替换为数组,过滤属性

    JSON.stringify(foo, ['week', 'month']);
    // '{"week":45,"month":7}', only keep "week" and "month" properties
    

    ③map,set, weakMap,weakSet 最好使用replacer 函数进行操作

    // Standard data structures
    JSON.stringify([new Set([1]), new Map([[1, 2]]), new WeakSet([{a: 1}]), new WeakMap([[{a: 1}, 2]])]);
    // '[{},{},{},{}]'
    
  2. slice 方法?

    返回浅拷贝的新数组

    slice(start, end) // 包头去尾
    
    //使用这个来转化类数组对象
    
    
    function list(arguments) {
      return [].slice.call(arguments);
    }
    
    //等价于下面 , 绑定了slice 方法的 sliceCall 函数。
    function list(arguments) {
        // call 是 (thisArg,参数列表) 去绑定参数列表
      return Function.prototype.call.bind([].slice))(arguments)
    }
    
    list(arguments)
    

    🛬 扩展问:

    什么是深浅拷贝? 这是对于Object 这种的引用对象。

    1. 浅拷贝:创建一个新对象,如果是基本类型值就赋值一份。否则如果是引用类型,就拷贝引用对象的内存地址。

    2. 深拷贝是开辟内存,从内存中完整的复制一份。特殊的是,如果遇到引用类型,递归的拷贝对象。

      浅拷贝与深拷贝方法?

      还有其他函数是浅拷贝的吗? Object.assign() Array.prototype.concat() splice

  3. splice 的意图?与slice 的差别

    splice(start, deleteCount, item1, item2, itemN)
    

    同时用来新增与删除 。

    Slice 【符合Fn 编程的纯函数概念】Splice [不符合函数式概念]
    Doesn't modify the original array(immutable)Modifies the original array(mutable)
    Returns the subset of original arrayReturns the deleted elements as array
    Used to pick the elements from arrayUsed to insert or delete elements to/from array
  4. 我们怎么比较Object 与 Map

    它们都可以用来存储key,value。删除查找是否有相应的key 存储。

    由于历史原因Object 被当作map 用。

    1. Object key只能用 string,Symbol . map 都可以包含 function,对象等
    2. map 是按照插入顺序排的。Object 不是
    3. map 可以轻松获取Keys 的长度,然而Object 必须手动遍历
  5. 箭头函数

箭头函数没有原型链,没有自己的this 与 arguments 。并且它的this 是指向声明的环境上下文。不能作为构造函数。

  1. 什么是高阶函数,一阶函数?高阶即返回函数,或者将函数作为参数。其余为一阶

    注意在高阶函数中使用箭头函数。例如 addEventListenersetTimeout this 始终绑定声明时的函数上下文 ,即window 对象。 注意:字母量 + 箭头函数,this 绑定字面量声明的函数上下文

  2. 什么是柯里化?将多参数函数,转会为多个单元函数的过程。【为什么需要?有利于逻辑复用】

  3. 纯函数? 返回值,只依赖于输入的参数,并且不会产生任何副作用。

  4. const , let 的意图 ? 块作用域与 (const)不可变

  5. 暂时性死区与变量提升? var 存在变量提升【函数作用域内】,constlet反之,在其之前英勇会造成Reference Error 。 内部函数如果找不到变量,会去外面找。

  6. IIFE [立马执行函数] (function())() 等..

  7. 为什么需要模块化【将相似逻辑代码封装在同一文件中】?可维护性,复用性,命名空间【杜绝全局变量】

    nodejs在文件中的命名的变量是不会挂载在全局中的。

  8. 什么是memoization ?是一种提高函数执行效率的方法。

const memoizeFn = (caculate) =>{
	let cache ={}
	//闭包函数
	return (value)=>{
	if(value in cache)
      return cache[value];
	else
      return cache[value] = caculate(value);		// 计算逻辑
	}
}

🚚 什么是闭包?闭包是被外层函数包裹的内层函数与声明该函数的词法环境。

原理? 因为无论何时创建函数,都会保持词法环境的引用(通过内置 [[Environment]]属性)。

作用? 可以用来创建私有变量,缓存值。内部(addEventListenersetTimeout)回调函数 来封装状态。

怎么释放? 将返回出来的函数变为null。

  1. 什么是hoisted【当前词法环境中】?

    指的是var function 生命会于函数作用域内,将声明过程放在最前

    Is ‘let’ really Not Hoisted? Lifecycle of variables | The Startup (medium.com)

    js 引擎上分为声明阶段(区别:声明变量),初始阶段(被赋值为undefined),赋值阶段变量被赋值。

    前两者为编译时期【var 经过前两阶段,然而let 只执行声明阶段】,后者为执行时期。

    总结:所有变量函数类声明都会hoisted,只是 let const 不能被引用在被声明之前。

    什么是词法环境?

    词法环境(lexical environment)是JavaScript引擎内部用来跟踪标识 符与特定变量之间的映射关系。

    查找变量的过程?

    因为我们需要访问外部代码结构中的变量,如果在当前环境中无法找到某一标识符,就会对外部环境进行查 找。一旦查找到匹配的变量,或是在全局环境中仍然无法查找到对应的 标识符而返回错误,就会停止查找。

    无论何时创建函数,都会创建一个与之相关联的词法环境,并存储 在名为[[Environment]]的内部属性上(也就是说无法直接访问或操 作)。

    总结:执行过程:创建词法环境,进入第一阶段,并不执行代码。注册声明在当前词法环境的变量与函数。然后进入第二阶段:

    1. 函数作用域1: 如果是函数环境,创建形参与形参默认值。
    2. 函数作用域2: 扫描函数作用域声明的函数【函数表达式或声明函数】,如果存在,将其重写
    3. 块级作用域: 声明当前函数作用域外声明的var 变量,以及当前作用域的const , let

    18 How do you redeclare variables in switch block without an error

    加括号,创建块级作用域

    1. How do you redeclare variables in switch block without an error

    JavaScript / encodeURIComponent — DevDocs

    let uri = "employeeDetails?name=john&occupation=manager";
    let encoded_uri = encodeURI(uri);
    let decoded_uri = decodeURI(encoded_uri);
    //如果想转化 特殊字符/ ? : @ & = + $ #
    encodeURIComponent()
    
    1. 什么是模块?为什么需要?

      好处,命名空间,可维护性,可复用性

      有哪些格式?ES module , commonjs , AMD 【异步模块定义】

      // es6
      export default {}
      export function add(){};
      exprot const a  =5;
      import * as from '';
      // common js --> 服务器环境
      module.exports = {}
      exports = module.exports
      exports.area = function(){}
      let a = require('')
      let {area} = require('')
      //AMD --> 浏览器环境
      define(['package/lib'], function(lib){
        function foo(){
          lib.log('hello world!');
        }
        return {
          foo: foo
        };
      });
      //AMD 兼容 commonJS
      
      
    2. ES module vs common js

      在编译时期运行,能够静态分析做Tress shaking . 异步加载。前者只读不可修改。

      后者是动态执行的,require可以写在 条件判断中。后者值拷贝,引用共享。可以修改引入的值。

    commonJS 和 ES Module 的区别 - SegmentFault 思否

    common 存在循环引用? 引入文件会全部执行。

    但是当循环引用,即全局中引入了之前的模块。就会从缓存中读取值,不会再去执行之前引入的模块。

    // a.js
    exports.x = 'a1';
    console.log('a.js ', require('./b.js').x);
    exports.x = 'a2';
    
    // b.js
    exports.x = 'b1';
    console.log('b.js ', require('./a.js').x);
    exports.x = 'b2';
    
    // main.js
    console.log('main.js ', require('./a.js').x);
    console.log('main.js ', require('./b.js').x);
    
    
    //执行结果
    $ node main.js
    b.js  a1
    a.js  b2
    main.js  a2
    main.js  b2
    
    1. 什么是作用域

      作用域决定对于变量与资源的可见性。作用域分为函数作用域与全局作用域。es6 的块级作用域。

    浏览器知识点

    1. 什么是 service work

    A Service worker is basically a script (JavaScript file) that runs in the background, separate from a web page and provides features that don't need a web page or user interaction. 主要是增加离线体验的

    1. 怎么使用 service work 操作 DOM

      可以使用PostMessage API 让主页面修改DOM

image-20220309160137694