JavaScript相关文章

JS原理性知识点(备试)

2019-12-20  本文已影响0人  前端辉羽

1.理解 bind、apply 和 call

希望使用某个上下文调用该函数,请使用 .bind() ,这在事件中很有用。 如果要立即调用函数,请使用.call() 或 .apply(),并修改上下文。
举例说明
假设你的数学老师要求你创建一个库并提交。你写了一个抽象的库,它可以求出圆的面积和周长:

var mathLib = {
    pi: 3.14,
    area: function(r) {
        return this.pi * r * r;
    },
    circumference: function(r) {
        return 2 * this.pi * r;
    }
};

提交后,老师调用了它:

mathLib.area(2);
12.56

老师发现他给你要求是 pi 精确到小数点后 5 位数而你只精确到 2 位, 现在由于最后期限已过你没有机会提交库。 这里 JS的 call 函数可以帮你, 只需要调用你的代码如下:

mathLib.area.call({pi: 3.14159}, 2)

它会动态地获取新的 pi 值,结果如下:

12.56636

这时,注意到 call 函数具有两个参数:

var cylinder = {
    pi: 3.14,
    volume: function(r, h) {
        return this.pi * r * r * h;
    }
};

调用方式如下:

cylinder.volume.call({pi: 3.14159}, 2, 6);
75.39815999999999

apply 类似,只是函数参数作为数组传递。

cylinder.volume.apply({pi: 3.14159}, [2, 6]);
75.39815999999999

如果你会使用 call 你基本就会用 apply 了,反之亦然, 那 bind 的用法又是如何呢 ?
bind 将一个全新的 this 注入到指定的函数上,改变 this 的指向, 使用 bind 时,函数不会像 call 或 apply 立即执行。

var newVolume = cylinder.volume.bind({pi: 3.14159});
newVolume(2,6); 
// Now pi is 3.14159

bind 用途是什么?它允许我们将上下文注入一个函数,该函数返回一个具有更新上下文的新函数。这意味着这个变量将是用户提供的变量,这在处理 JavaScript 事件时非常有用。

2.理解 js 作用域(闭包)

全局作用域示例如下:

x = 10;
function Foo() {
  console.log(x); // Prints 10
}
Foo()

函数作用域生效当你定义一个局部变量时:

pi = 3.14;
function circumference(radius) {    
     pi = 3.14159;
     console.log(2 * pi * radius); 
     // 打印 "12.56636" 不是 "12.56"
}
circumference(2);

ES6 标准引入了新的块作用域,它将变量的作用域限制为给定的括号块。

var a = 10;
function Foo() {
  if (true) {
    let a = 4;
  }
  alert(a); // alerts '10' because the 'let' keyword
}
Foo();

函数和条件都被视为块。以上例子应该弹出 4,因为 if 已执行。但 是ES6 销毁了块级变量的作用域,作用域进入全局。
现在来到神奇的作用域,可以使用闭包来实现,JavaScript 闭包是一个返回另一个函数的函数。
如果有人问你这个问题,编写一个输入一个字符串并逐次返回字符。 如果给出了新字符串,则应该替换旧字符串,类似简单的一个生成器。

function generator(input) {
    var index = 0;
    return {
      next: function() {
        if (index < input.length) {
          index += 1;
          return input[index - 1];
        }
        return "";
      } 
    }·
  }

执行如下:

var mygenerator = generator("boomerang");
mygenerator.next(); // returns "b"
mygenerator.next() // returns "o"
mygenerator = generator("toon");
mygenerator.next(); // returns "t"

3.JS原始数据类型有哪些?引用数据类型有哪些?

在 JS 中,存在着 7 种原始值,分别是:

引用数据类型: 对象Object(包含普通对象-Object,数组对象-Array,正则对象-RegExp,日期对象-Date,数学函数-Math,函数对象-Function)

4.null是对象吗?为什么?

结论: null不是对象。
解释: 虽然 typeof null 会输出 object,但是这只是 JS 存在的一个悠久 Bug。在 JS 的最初版本中使用的是 32 位系统,为了性能考虑使用低位存储变量的类型信息,000 开头代表是对象然而 null 表示为全零,所以将它错误的判断为 object 。

5.JavaScript中有哪些异步编程方式?

1.回调函数

f1(f2);

回调函数是异步编程的基本方法。其优点是易编写、易理解和易部署;缺点是不利于代码的阅读和维护,各个部分之间高度耦合 (Coupling),流程比较混乱,而且每个任务只能指定一个回调函数。

  1. 事件监听
f1.on('done',f2);

事件监听即采用事件驱动模式,任务的执行不取决于代码的顺序,而取决于某个事件是否发生。其优点是易理解,可以绑定多个事件,每个事件可以指定多个回调函数,可以去耦合, 有利于实现模块化;缺点是整个程序都要变成事件驱动型,运行流程会变得不清晰。

  1. 发布/订阅
f1: jQuery.publish("done");
f2: jQuery.subscribe("done", f2);

假定存在一个"信号中心",某个任务执行完成,就向信号中心"发布"(publish)一个信号,其他任务可以向信号中心"订阅"(subscribe)这个信号,从而知道什么时候自己可以开始执行,这就叫做 "发布/订阅模式" (publish-subscribe pattern),又称 "观察者模式" (observer pattern)。该 方法的性质与"事件监听"类似,但其优势在于可以 通过查看"消息中心",了解存在多少信号、每个信号有多少订阅者,从而监控程序的运行。

  1. promise对象
f1().then(f2);

Promises对象是CommonJS工作组提出的一种规范,目的是为异步编程提供 统一接口 ;思想是, 每一个异步任务返回一个Promise对象,该对象有一个then方法,允许指定回调函数。其优点是回调函数是链式写法,程序的流程非常清晰,而且有一整套的配套方法, 可以实现许多强大的功能,如指定多个回调函数、指定发生错误时的回调函数, 如果一个任务已经完成,再添加回调函数,该回调函数会立即执行,所以不用担心是否错过了某个事件或信号;缺点就是编写和理解相对比较难。

6.script 标签中的defer 和 async 异同点?

defer

这个属性的用途是表明脚本在执行时不会影响页面的构造。也就是说,脚本会被延迟到整个页面都解析完毕后再运行。因此,在script元素中设置defer属性,相当于告诉浏览器立即下载,但延迟执行。
HTML5规范要求脚本按照它们出现的先后顺序执行,因此第一个延迟脚本会先于第二个延迟脚本执行,而这两个脚本会先于DOMContentLoaded事件执行。在现实当中,延迟脚本并不一定会按照顺序执行,也不一定会在DOMContentLoad时间触发前执行,因此最好只包含一个延迟脚本。
对于不支持的浏览器,如safari,并不会延迟执行,还是会阻塞浏览器渲染。

async

这个属性与defer类似,都用于改变处理脚本的行为。同样与defer类似,async只适用于外部脚本文件,并告诉浏览器立即下载文件。但与defer不同的是,标记为async的脚本并不保证按照它们的先后顺序执行。
第二个脚本文件可能会在第一个脚本文件之前执行。因此确保两者之间互不依赖非常重要。指定async属性的目的是不让页面等待两个脚本下载和执行,从而异步加载页面其他内容。

总结
async和defer都是script标签的属性,使用方法是直接加在标签中就可以了

<script type="text/javascript" src="js/1.js" defer></script>
<script type="text/javascript" src="js/1.js" async></script>

async 和 defer 虽然都是异步的,不过还有一些差异,使用 async 标志的脚本文件一旦加载完成,会立即执行;而使用了 defer 标记的脚本文件,需要在 DOMContentLoaded 事件之前执行。

7.如何理解回流和重绘??

回流:当我们对 DOM 的修改引发了 DOM 几何尺寸的变化(比如修改元素的宽、高或隐藏元素等)时,浏览器需要重新计算元素的几何属性(其他元素的几何属性和位置也会因此受到影响),然后再将计算的结果绘制出来。这个过程就是回流(也叫重排)。

重绘:当我们对 DOM 的修改导致了样式的变化、却并未影响其几何属性(比如修改了颜色或背景色)时,浏览器不需重新计算元素的几何属性、直接为该元素绘制新的样式(跳过了上图所示的回流环节)。这个过程叫做重绘。由此我们可以看出,重绘不一定导致回流,回流一定会导致重绘。

常见的会导致回流的元素:

避免方式:
1.避免逐条改变样式,使用类名去合并样式
2.将 DOM “离线”,使用DocumentFragment
3.提升为合成层,如使用will-change

#divId {
  will-change: transform;
}

优点

注意:
部分浏览器缓存了一个 flush 队列,把我们触发的回流与重绘任务都塞进去,待到队列里的任务多起来、或者达到了一定的时间间隔,或者“不得已”的时候,再将这些任务一口气出队。但是当我们访问一些即使属性时,浏览器会为了获得此时此刻的、最准确的属性值,而提前将 flush 队列的任务出队。

上一篇下一篇

猜你喜欢

热点阅读