Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

js基础知识 #4

Open
lyre2468 opened this issue Nov 13, 2018 · 0 comments
Open

js基础知识 #4

lyre2468 opened this issue Nov 13, 2018 · 0 comments

Comments

@lyre2468
Copy link
Owner

lyre2468 commented Nov 13, 2018

解释 JavaScript 中的值和类型,哪些是值类型(传值)、引用类型(传址)

  • string
  • number
  • boolean
  • null 和 undefined
  • object(引用类型
  • symbol (ES6 中新增的)

null、 undefined、not defined区别

  • 如果你试图使用一个不存在且尚未声明的变量a,JavaScript 将抛出错误“a is not defined”,让后脚本将停止运行; 此时 typeof a = "undefined"
  • 定义了变量b(即var b;), 使用b,输出 undefined;此时 typeof b = "undefined"
  • null表示“没有对象”,即此处不该有值。典型用法是:
    • (1) 作为函数的参数,表示该函数的参数不是对象。
    • (2) 作为对象原型链的终点 Object.getPrototypeOf(Object.prototype)

下面的代码 连续赋值 foo.x是什么

var foo = {n: 1};
var bar = foo;
foo.x = foo = {n: 2};
console.log(foo.x)

foo.x = undefined bar.x = {n:2}

js中连续赋值是 右结合

一行代码如何数组去重

new Set([1,1,2,'e',111,1,2,2,1])

javascript中== 和 === 区别

  • 严格比较(例如 ===)在不允许强制转型的情况下检查两个值是否相等;
  • 抽象比较(例如 ==)在允许强制转型的情况下检查两个值是否相等。

typeof 和instanceof区别

  • typeof返回数据的类型,返回一个字符串
    • typeof null = "object" (注意)
    • typeof undefined = "undefined"
    • typeof { a: 1} = "object"
    • typeof function() {} = "function"
  • a instanceof A判读a是否是某个对象A的实例(用来判断某个构造函数的prototype属性是否存在于另外一个要检测对象的原型链上

什么是高阶函数?如map/reduce/filter/sort

  • 函数可以作为参数被传递
  • 函数可以作为返回值输出

数组常用的方法?有什么区别?

数组

“use strict”的作用是什么?

use strict 出现在 JavaScript 代码的顶部或函数的顶部,可以帮助你写出更安全的 JavaScript 代码。

  • 显式报错

    • 只读属性不可写
    • 函数不能有重名参数
    • 禁止八进制
  • 增强的安全措施

    • 变量必须声明

    • 禁止this 指向全局变量

      // 严格模式
      function f() {
        'use strict';
        console.log(this === undefined);
      }
      f() // true
    • 禁止使用fn.callee 、 fn.caller、arguments.callee、arguments.caller

    • 禁止删除变量

  • 静态绑定

    • 禁止with语句
    • 创设eval作用域
    • arguments不再跟踪参数的变化
  • 向下一个版本的js过渡

具体见 https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Strict_mode

javascript中采用 词法作用域(静态作用域)

作用域是指程序源代码中定义变量的区域。词法作用域是说函数的作用域在函数定义的时候就决定了。

var value = 1;

function foo() {
    console.log(value);
}

function bar() {
    var value = 2;
    foo();
}

bar();
// 输出 2

// 案例2
var scope = "global scope";
function checkscope(){
    var scope = "local scope";
    function f(){
        return scope;
    }
    return f();
}
checkscope(); // 输出 local scope

// 案例3
var scope = "global scope";
function checkscope(){
    var scope = "local scope";
    function f(){
        return scope;
    }
    return f;
}
checkscope()(); // 输出 local scope

案例2 和 案例3的区别???执行上下文栈的变化不一样

this, call, apply 和 bind

如何实现 Function.prototype.bind/apply/bind

思考 : 执行 fn.bind(obj)发生了什么?

执行fn函数(这里好说),将fn的作用域绑定到obj(?)

Function.prototype.bind = function(content) {
    // 这里执行的时候this-> 当前function
    // 改变function的作用域,将fucntion挂到content上
    content.fn = this;
    content.fn();
  	delete content.fn;
}

具体更好解决办法见jawil/blog#16

commonjs amd umd es6模块之前的区别

模块化是软件系统的属性,这个系统被分解为一组高内聚,低耦合的模块。

一个模块化系统所需要的能力:

  • 定义封装的模块。
  • 定义新模块对其他模块的依赖。
  • 可对其他模块的引入支持。
  commonjs amd规范 cmd规范 umd es6
特点 需要执行整个js获取依赖关系;但只执行一次,之后加载从缓存获取 RequireJS 提前执行,前置依赖 Sea.js 延迟执行,就近依赖 AMD和CommonJS的糅合 静态分析, 动态引用(按需加载)
使用 exports module.exports require define(id?, dependencies?, factory); require require([module], callback)   export import
异步/同步 同步 异步 异步 异步 异步
环境 server browser browser browser server/browser

jquery ready如何实现

document.readyState === "complete"   (onreadystatechange)
document.addEventListener( "DOMContentLoaded", completed );
document.addEventListener( "load", completed ); // onload 是所有的资源加载完成

为什么执行IIFE function foo(){ }(); 会报错

js解析成function foo(){ }()function foo(){ }是语句,不是表达式, 不能调用;然后()想去执行函数,所以会抛出Uncaught SyntaxError: Unexpected token )

正确调用立即调用表达式的方式如下:

  • (function foo(){ alert(1)}())
  • (function foo(){ alert(1)})()
  • void function bar() { alert(1) }()
表达式和语句
  • expressions : 会产生一个值
  • statements: 完成某项任务的操作

http://2ality.com/2012/09/expressions-vs-statements.html

function Person(){}, var person = Person(), and var person = new Person()有什么区别?

闭包

原型继承是怎样工作的/请解释原型设计模式

当new一个对象的时候,如f = new F(),f会继承F.prototype上的方法。意味着,当在f上找不到属性时,js引擎会到F.prototype 、F.prototye的原型上查找属性,如果找不到,直到最顶端的null(Object.prototype.__ proto __)。

区别 prototype 和 __ proto __

  • prototype是对象上 Obj的属性,继承Obj 都可以继承到Obj.prototype上的属性。
  • __ proto __ 是指向,对象__ proto __属性的值就是它所对应的原型对象

有一点像鸡和蛋的问题

prototype

ES5如何实现继承

  • 组合继承式(用原型实现继承,构造函数实现传参数)

    缺点:调用了两次构造函数

    function Super(name) {
        this.name=name;
    }
    Super.prototype.sayName = function() {
        console.log(this.name);
    }
    function Sub(name){
      Super.call(this, name)   // 利用call、apply调用父类的构造函数
    }
    Sub.prototype = new Super(); // 原型
    // 创建实例
    var a = new Sub('xiaozhang')
    a.sayName() // 'xiaozhang'
  • 寄生组合式继承。借用构造函数 继承 属性, 原型链 继承 方法

     if(!Object.create) {
         Object.create = function (o) {
             var F = function() {};
             F.prototype = o;
             return new F();
         };
     }
     function inheritp(sub, supers) {
         var prototype = Object.create(supers.prototype); // 创建对象
         prototype.constructor = sub; // 增强对象
         sub.prototype = prototype; //  原型指向
     };
    
      // Super
      function Super(name) {
          this.name=name;
      }
      Super.prototype.sayName = function() {
          console.log(this.name);
      }
      function Sub(name) {
         Super.call(this, name) // 利用call、apply调用父类的构造函数
             // name和study位于实例上。 +1,只调用了一次父类的构造函数.
      };
      inheritp(Sub, Super);
      var instance = new Sub('Lucy');
      instance.sayName();    // Lucy

    ​#### new 做了什么

var a = new A();

  1. 创建一个空对象,作为将要返回的对象实例。 t= {}
  2. 将这个空对象的原型,指向构造函数的prototype属性。t.__proto__ = A.prototype
  3. 将这个空对象赋值给函数内部的this关键字。t.call(this)
  4. 开始执行构造函数内部的代码。t()

es6 新特性

  • 新增数据类型Symbol

  • 变量声明 let const

  • 箭头函数

    • 箭头函数表达式的语法比函数表达式更短,并且没有自己的thisargumentssupernew.target。这些函数表达式更适用于那些本来需要匿名函数的地方,并且它们不能用作构造函数。

    • 由于 箭头函数没有自己的this指针,通过 call() apply() 方法调用一个函数时,只能传递参数(不能绑定this---译者注),他们的第一个参数会被忽略。

      var adder = {
        base : 1,  
        add : function(a) {
          var f = v => v + this.base;
          return f(a);
        },
        addThruCall: function(a) {
          var f = v => v + this.base;
          var b = {
            base : 2
          };         
          return f.call(b, a);
        }
      };
      console.log(adder.add(1));         // 输出 2
      console.log(adder.addThruCall(1)); // 仍然输出 2(而不是3 ——译者注)
  • 块级作用域

  • 字符串模版

  • 关键字类和继承 class extends

  • 模块化export / import

  • Generators

  • 新的数据结构Set / Map

    // Set 类似于数组,但是成员的值都是唯一的
    // WeapSet  成员只能是对象
    
    // Map 类似于对象,但是“键”的范围不限于字符串,各种类型的值(包括对象)都可以当作键
    // WeakMap 只接受对象作为键名(null除外); 键名所指向的对象,不计入垃圾回收机制
  • 解构赋值 / 延展操作符

es6 es7 es8

  • es6: 见es6特性

  • es7: Array.prototype.includes, 指数运算符**

  • es8: async/await, Object.values(), Object.entries, String.prototype.padStartString.prototype.padEnd,Object.getOwnPropertyDescriptors, 函数参数列表结尾允许逗号

https://developer.mozilla.org/en-US/docs/Web/JavaScript/New_in_JavaScript/ECMAScript_Next_support_in_Mozilla#ECMAScript_2017

babel如何实现(换一个说法,在编译之后插入部分代码))

js的异步操作有哪些

  • ajax
  • dom事件监听
  • setTimeout/setInterval
  • promise
  • 回调函数
  • generator
  • async/await

下面的代码输出什么

function a() {
	var ms = 60000 + new Date().getTime();
    while( 
      new Date().getTime() < ms
    ) {}
    console.log('finish')
}

document.addEventListener('click', function() {
	console.log((new Date().getTime()- startTime) +'click event')
})

var startTime = new Date().getTime();

setTimeout(() => { 
  	console.log( (new Date().getTime()-startTime) + 'setTimeout1' 
)}, 2000);

console.log('dddd')

a();

setTimeout(() => { 
  	console.log( (new Date().getTime()-startTime) + 'setTimeout2' 
)}, 2000);

console.log('finish-1');

// 在60s内连续点击多次,执行的结果

调用堆栈(call stack)、event loop

浏览器和node两端不一样

https://juejin.im/post/5a05b4576fb9a04519690d42#comment

浏览器中的事件循环如下

js事件循环

任务队列

  • 宏任务:script(全局任务), setTimeout, setInterval, setImmediate, I/O, UI rendering.
  • 微任务:process.nextTick, Promise, Object.observer, MutationObserver.

js引擎(engine): 解释并编译代码,让它变成能交给机器运行的代码

js运行(runtime): 就是运行环境,它提供一些对外接口供Js调用,以跟外界打交道,比如,浏览器环境、Node.js环境。

浏览器的 Event Loop 遵循的是 HTML5 标准, 会无限循环执行下面3步,这就是Event Loop

的主要控制逻辑。

  1. 取一个宏任务来执行。执行完毕后,下一步。
  2. 取一个微任务来执行,执行完毕后,再取一个微任务来执行。直到微任务队列为空,执行下一步。
  3. 更新UI渲染。

Node中的事件循环

node中的事件循环

  • timers: 这个阶段执行setTimeout()setInterval()设定的回调。
  • pending callbacks: 上一轮循环中有少数的 I/O callback 会被延迟到这一轮的这一阶段执行。
  • idle, prepare: 仅内部使用。
  • poll: 执行 I/O callback,在适当的条件下会阻塞在这个阶段
  • check: 执行setImmediate()设定的回调。
  • close callbacks: 执行比如socket.on('close', ...)的回调。

浅拷贝和深拷贝(对象和数组)

  • 浅拷贝: 将b拷贝给a, a=b, a和b指向了同一块内存,所以修改其中任意的值,另一个值都会随之变化。

    // Object.assign使用了浅拷贝
    var target = {a: 1, b: 1};
    var copy1 = {a: 2, b: 2, c: {ca: 21, cb: 22, cc: 23}};
    var copy2 = {c: {ca: 31, cb: 32, cd: 34}};
    var result = Object.assign(target, copy1, copy2);
    console.log(target);    // {a: 2, b: 2, c: {ca: 31, cb: 32, cc: 33}}
    console.log(target === result);    // true
  • 深拷贝:如果给b放到新的内存中,将a的各个属性都复制到新内存里,b的改变不会引起a的改变

    // jQuery.extend() 是深拷贝
    jQuery.extend = jQuery.fn.extend = function() {
        var options, name, src, copy, copyIsArray, clone,
            target = arguments[ 0 ] || {},
            i = 1,
            length = arguments.length,
            deep = false;
     
        // Handle a deep copy situation
        if ( typeof target === "boolean" ) {
            deep = target;
     
            // Skip the boolean and the target
            target = arguments[ i ] || {};
            i++;
        }
     
        // Handle case when target is a string or something (possible in deep copy)
        if ( typeof target !== "object" && !jQuery.isFunction( target ) ) {
            target = {};
        }
     
        // Extend jQuery itself if only one argument is passed
        if ( i === length ) {
            target = this;
            i--;
        }
     
        for ( ; i < length; i++ ) {
     
            // Only deal with non-null/undefined values
            if ( ( options = arguments[ i ] ) != null ) {
     
                // Extend the base object
                for ( name in options ) {
                    src = target[ name ];
                    copy = options[ name ];
     
                    // Prevent never-ending loop
                    if ( target === copy ) {
                        continue;
                    }
     
                    // Recurse if we're merging plain objects or arrays
                    if ( deep && copy && ( jQuery.isPlainObject( copy ) || ( copyIsArray = Array.isArray( copy ) ) ) ) {
     
                        if ( copyIsArray ) {
                            copyIsArray = false;
                            clone = src && Array.isArray( src ) ? src : [];
     
                        } else {
                            clone = src && jQuery.isPlainObject( src ) ? src : {};
                        }
     
                        // Never move original objects, clone them
                        target[ name ] = jQuery.extend( deep, clone, copy );
     
                    // Don't bring in undefined values
                    } else if ( copy !== undefined ) {
                        target[ name ] = copy;
                    }
                }
            }
        }
        // Return the modified object
        return target;
    };

setTimeout, setInterval 和 requestAnimationFrame

  • seTimeout 实现的动画在某些低端机上会出现卡顿、抖动的现象

    • setTimeout 的执行时间并不是确定的。在JavaScript中, setTimeout 任务被放进了异步队列中,只有当主线程上的任务执行完以后,才会去检查该队列里的任务是否需要开始执行,所以 setTimeout 的实际执行时机一般要比其设定的时间晚一些。
    • 刷新频率受 **屏幕分辨率 **和 **屏幕尺寸 **的影响,不同设备的屏幕绘制频率可能会不同,而 setTimeout 只能设置一个固定的时间间隔,这个时间不一定和屏幕的刷新时间相同。
  • requestAnimationFrame由系统来决定回调函数的执行时机

    requestAnimationFrame的执行步伐跟着系统的绘制频率(系统绘制率一般是 60Hz)走。它能保证回调函数在屏幕每一次的绘制间隔中只被执行一次,这样就不会引起丢帧现象,也不会导致动画出现卡顿的问题

    • 不会丢帧,不会卡顿
    • CPU节能。当页面处理未激活的状态下,该页面的屏幕绘制任务也会被系统暂停,因此跟着系统步伐走的 requestAnimationFrame也会停止渲染,当页面被激活时,动画就从上次停留的地方继续执行,有效节省了 CPU 开销
    • 函数节流。使用 requestAnimationFrame 可保证每个绘制间隔内,函数只被执行一次
    // 兼容,优雅降级
    window.requestAnimFrame = (function(){
      return  window.requestAnimationFrame       ||
              window.webkitRequestAnimationFrame ||
              window.mozRequestAnimationFrame    ||
              function( callback ){
                window.setTimeout(callback, 1000 / 60);
              };
    })();

JavaScript Memoization

memoization 是一种将函数返回值缓存起来的方法

纯函数

内存回收机制有哪几种?

  • 标记清除
  • 引用计数

有哪些操作会引起内存泄漏

  • 意外的全局变量
  • 闭包
  • 没有清理的DOM元素引用
  • 被遗忘的定时器或者回调
  • 子元素存在引起的内存泄露

正则表达式

柯里化

柯里化是一种将使用多个参数的一个函数转换成一系列使用一个参数的函数的技术。

好处

  1. 参数复用
  2. 提前返回;
  3. 延迟计算/运行

var fn=function(a,b,c){
​ return a+b+c;
}
需要写一个函数,满足
curry(fn)(1)(2)(3) //6

function curry(fn) {
    var arr = [],
    mySlice = arr.slice
    fnLen = fn.length;

    // console.log('2',arguments, arr, fnLen)
    function curring() {
        arr = arr.concat(mySlice.call(arguments));
        // console.log(arguments, arr)
        if(arr.length < fnLen) {
            return curring;
        }
        return fn.apply(this, arr);
    }
    return curring
}

JavaScript 自定义事件

var event = document.createEvent(type);

// 创建事件
var event = document.createEvent('Event');

// 定义事件名为'build'.
event.initEvent('build', true, true);

// 监听事件
var elem = document.getElementById('testBox');
elem.addEventListener('build', function (e) {
  // e.target matches elem
}, false);

// 触发对象可以是任何元素或其他事件目标
elem.dispatchEvent(event);

https://developer.mozilla.org/zh-CN/docs/Web/API/Document/createEvent#Notes

@lyre2468 lyre2468 changed the title js js基础知识 Nov 13, 2018
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant