js相关面试题20道

21. for in、Object.keys 和 Object.getOwnPropertyNames 对属性遍历有什么区别?

for in 会遍历自身及原型链上的可枚举属性
Object.keys 会将对象自身的可枚举属性的 key 输出
会将自身所有的属性的 key 输出
解析:ECMAScript 将对象的属性分为两种:数据属性和访问器属性。var parent = Object.create(Object.prototype, {
a: {
value: 123,
writable: true,
enumerable: true,
configurable: true
}
});
// parent继承自Object.prototype,有一个可枚举的属性a(enumerable:true)。

var child = Object.create(parent, {
b: {
value: 2,
writable: true,
enumerable: true,
configurable: true
},
c: {
value: 3,
writable: true,
enumerable: false,
configurable: true
}
});
//child 继承自 parent ,b可枚举,c不可枚举for infor (var key in child) {
console.log(key);
}
// b
// a
// for in 会遍历自身及原型链上的可枚举属性如果只想输出自身的可枚举属性,可使用 hasOwnProperty 进行判断(数组与对象都可以,此处用数组做例子)let arr = [1, 2, 3];
Array.prototype.xxx = 1231235;
for (let i in arr) {
if (arr.hasOwnProperty(i)) {
console.log(arr[i]);
}
}
// 1
// 2
// 3Object.keysconsole.log(Object.keys(child));
// [“b”]
// Object.keys 会将对象自身的可枚举属性的key输出Object.getOwnPropertyNamesconsole.log(Object.getOwnPropertyNames(child));
// [“b”,“c”]
// 会将自身所有的属性的key输出

22.iframe 跨域通信和不跨域通信

不跨域通信主页面‹!DOCTYPE html›
‹html›
‹head›
‹meta charset=“utf-8” /›
‹title›‹/title›
‹/head›
‹body›
‹iframe
name=“myIframe”
id=“iframe”
class=""
src=“flexible.html”
width=“500px”
height=“500px”

‹/iframe›
‹/body›
‹script type=“text/javascript” charset=“utf-8”›
function fullscreen() {
alert(1111);
}
‹/script›
‹/html›子页面 flexible.html‹!DOCTYPE html›
‹html›
‹head›
‹meta charset=“utf-8” /›
‹title›‹/title›
‹/head›
‹body›
我是子页面
‹/body›
‹script type=“text/javascript” charset=“utf-8”›
// window.parent.fullScreens()
function showalert() {
alert(222);
}
‹/script›
‹/html›1、主页面要是想要调取子页面的 showalert 方法myIframe.window.showalert();2、子页面要掉主页面的 fullscreen 方法window.parent.fullScreens();3、js 在 iframe 子页面获取父页面元素:window.parent.document.getElementById(“元素id”);4、js 在父页面获取 iframe 子页面元素代码如下:window.frames[“iframe_ID”].document.getElementById(“元素id”);跨域通信使用postMessage(官方用法)子页面window.parent.postMessage(“hello”, “http://127.0.0.1:8089”);父页面接收window.addEventListener(“message”, function(event) {
alert(123);
});

23.H5 与 Native 如何交互

jsBridge

24.如何判断一个对象是否为数组

第一种方法:使用 instanceof 操作符。第二种方法:使用 ECMAScript 5 新增的 Array.isArray()方法。第三种方法:使用使用 Object.prototype 上的原生 toString()方法判断。
25.

1、defer 和 async 的网络加载过程是一致的,都是异步执行。
2、区别在于加载完成之后什么时候执行,可以看出 defer 是文档所有元素解析完成之后才执行的。
3、如果存在多个 defer 脚本,那么它们是按照顺序执行脚本的,而 async,无论声明顺序如何,只要加载完成就立刻执行
解析:无论‹script›标签是嵌入代码还是引用外部文件,只要不包含 defer 属性和 async 属性(这两个属性只对外部文件有效),浏览器会按照‹script›的出现顺序对他们依次进行解析,也就是说,只有在第一个‹script›中的代码执行完成之后,浏览器才会执行第二个‹script›中的代码,并且在解析时,页面的处理会暂时停止。嵌入代码的解析=执行
外部文件的解析=下载+执行script 标签存在两个属性,defer 和 async,这两个属性只对外部文件有效只有一个脚本的情况‹script src=“a.js” /›没有 defer 或 async 属性,浏览器会立即下载并执行相应的脚本,并且在下载和执行时页面的处理会停止。‹script defer src=“a.js” /›有了 defer 属性,浏览器会立即下载相应的脚本,在下载的过程中页面的处理不会停止,等到文档解析完成脚本才会执行。‹script async src=“a.js” /›有了 async 属性,浏览器会立即下载相应的脚本,在下载的过程中页面的处理不会停止,下载完成后立即执行,执行过程中页面处理会停止。‹script defer async src=“a.js” /›如果同时指定了两个属性,则会遵从 async 属性而忽略 defer 属性。下图可以直观的看出三者之间的区别:其中蓝色代表 js 脚本网络下载时间,红色代表 js 脚本执行,绿色代表 html 解析。多个脚本的情况这里只列举两个脚本的情况:‹script src=“a.js”›‹/script›
‹script src=“b.js”›‹/script›没有 defer 或 async 属性,浏览器会立即下载并执行脚本 a.js,在 a.js 脚本执行完成后才会下载并执行脚本 b.js,在脚本下载和执行时页面的处理会停止。‹script defer src=“a.js”›‹/script›
‹script defer src=“b.js”›‹/script›有了 defer 属性,浏览器会立即下载相应的脚本 a.js 和 b.js,在下载的过程中页面的处理不会停止,等到文档解析完成才会执行这两个脚本。HTML5 规范要求脚本按照它们出现的先后顺序执行,因此第一个延迟脚本会先于第二个延迟脚本执行,而这两个脚本会先于 DOMContentLoaded 事件执行。
在现实当中,延迟脚本并不一定会按照顺序执行,也不一定会在 DOMContentLoaded 事件触发前执行,因此最好只包含一个延迟脚本。‹script async src=“a.js”›‹/script›
‹script async src=“b.js”›‹/script›有了 async 属性,浏览器会立即下载相应的脚本 a.js 和 b.js,在下载的过程中页面的处理不会停止,a.js 和 b.js 哪个先下载完成哪个就立即执行,执行过程中页面处理会停止,但是其他脚本的下载不会停止。标记为 async 的脚本并不保证按照制定它们的先后顺序执行。异步脚本一定会在页面的 load 事件前执行,但可能会在 DOMContentLoaded 事件触发之前或之后执行。

26.Object.prototype.toString.call() 和 instanceOf 和 Array.isArray() 区别好坏

Object.prototype.toString.call()

优点:这种方法对于所有基本的数据类型都能进行判断,即使是 null 和 undefined 。

缺点:不能精准判断自定义对象,对于自定义对象只会返回[object Object]

instanceOf

优点:instanceof 可以弥补 Object.prototype.toString.call()不能判断自定义实例化对象的缺点。
缺点: instanceof 只能用来判断对象类型,原始类型不可以。并且所有对象类型 instanceof Object 都是 true,且不同于其他两种方法的是它不能检测出 iframes。

Array.isArray()

优点:当检测 Array 实例时,Array.isArray 优于 instanceof ,因为 Array.isArray 可以检测出 iframes
缺点:只能判别数组

解析:Object.prototype.toString.call()每一个继承 Object 的对象都有 toString 方法,如果 toString 方法没有重写的话,会返回 [Object type],其中 type 为对象的类型。但当除了 Object 类型的对象外,其他类型直接使用 toString 方法时,会直接返回都是内容的字符串,所以我们需要使用 call 或者 apply 方法来改变 toString 方法的执行上下文。const an = [“Hello”, “An”];
an.toString(); // “Hello,An”
Object.prototype.toString.call(an); // "[object Array]"这种方法对于所有基本的数据类型都能进行判断,即使是 null 和 undefined 。Object.prototype.toString.call(“An”); // “[object String]”
Object.prototype.toString.call(1); // “[object Number]”
Object.prototype.toString.call(Symbol(1)); // “[object Symbol]”
Object.prototype.toString.call(null); // “[object Null]”
Object.prototype.toString.call(undefined); // “[object Undefined]”
Object.prototype.toString.call(function() {}); // “[object Function]”
Object.prototype.toString.call({ name: “An” }); // "[object Object]"缺点:不能精准判断自定义对象,对于自定义对象只会返回[object Object]function f(name) {
this.name = name;
}
var f1 = new f(“martin”);
console.log(Object.prototype.toString.call(f1)); //[object Object]

Object.prototype.toString.call(); // 常用于判断浏览器内置对象。instanceofinstanceof 的内部机制是通过判断对象的原型链中是不是能找到类型的 prototype。使用 instanceof 判断一个对象是否为数组,instanceof 会判断这个对象的原型链上是否会找到对应的 Array 的原型,找到返回 true,否则返回 false。[] instanceof Array; // true但 instanceof 只能用来判断对象类型,原始类型不可以。并且所有对象类型 instanceof Object 都是 true。[] instanceof Object; // true优点:instanceof 可以弥补 Object.prototype.toString.call()不能判断自定义实例化对象的缺点。缺点:instanceof 只能用来判断对象类型,原始类型不可以。并且所有对象类型 instanceof Object 都是 true,且不同于其他两种方法的是它不能检测出 iframes。function f(name) {
this.name = name;
}
var f1 = new f(“martin”);
console.log(f1 instanceof f); //trueArray.isArray()

功能:用来判断对象是否为数组

instanceof 与 isArray

当检测 Array 实例时,Array.isArray 优于 instanceof ,因为 Array.isArray 可以检测出 iframesvar iframe = document.createElement(“iframe”);
document.body.appendChild(iframe);
xArray = window.frames[window.frames.length - 1].Array;
var arr = new xArray(1, 2, 3); // [1,2,3]

// Correctly checking for Array
Array.isArray(arr); // true
Object.prototype.toString.call(arr); // true
// Considered harmful, because doesn’t work though iframes
arr instanceof Array; // false缺点:只能判别数组
Array.isArray() 与 Object.prototype.toString.call()
Array.isArray()是 ES5 新增的方法,当不存在 Array.isArray() ,可以用 Object.prototype.toString.call() 实现。if (!Array.isArray) {
Array.isArray = function(arg) {
return Object.prototype.toString.call(arg) === “[object Array]”;
};
}

27.什么是面向对象?

面向对象是把构成问题事务分解成各个对象,建立对象的目的不是为了完成一个步骤,而是为了描叙某个事物在整个解决问题的步骤中的行为。解析:
面向对象和面向过程的异同

面向过程就是分析出解决问题所需要的步骤,然后用函数把这些步骤一步一步实现,使用的时候一个一个依次调用就可以了。
面向对象是把构成问题事务分解成各个对象,建立对象的目的不是为了完成一个步骤,而是为了描叙某个事物在整个解决问题的步骤中的行为。

28.你对松散类型的理解

JavaScript 中的变量为松散类型,所谓松散类型就是指当一个变量被申明出来就可以保存任意类型的值,就是不像 SQL 一样申明某个键值为 int 就只能保存整型数值,申明 varchar 只能保存字符串。一个变量所保存值的类型也可以改变,这在 JavaScript 中是完全有效的,只是不推荐。相比较于将变量理解为“盒子“,《JavaScript 编程精解》中提到应该将变量理解为“触手”,它不保存值,而是抓取值。这一点在当变量保存引用类型值时更加明显。JavaScript 中变量可能包含两种不同的数据类型的值:基本类型和引用类型。基本类型是指简单的数据段,而引用类型指那些可能包含多个值的对象。

我的手机 2020/4/6 20:49:09

29.JS 严格模式和正常模式

严格模式使用"use strict";作用:
消除 Javascript 语法的一些不合理、不严谨之处,减少一些怪异行为;
消除代码运行的一些不安全之处,保证代码运行的安全;
提高编译器效率,增加运行速度;
为未来新版本的 Javascript 做好铺垫。
表现:
严格模式下, delete 运算符后跟随非法标识符(即 delete 不存在的标识符),会抛出语法错误; 非严格模式下,会静默失败并返回 false
严格模式中,对象直接量中定义同名属性会抛出语法错误; 非严格模式不会报错
严格模式中,函数形参存在同名的,抛出错误; 非严格模式不会
严格模式不允许八进制整数直接量(如:023)
严格模式中,arguments 对象是传入函数内实参列表的静态副本;非严格模式下,arguments 对象里的元素和对应的实参是指向同一个值的引用
严格模式中 eval 和 arguments 当做关键字,它们不能被赋值和用作变量声明
严格模式会限制对调用栈的检测能力,访问 arguments.callee.caller 会抛出异常
严格模式 变量必须先声明,直接给变量赋值,不会隐式创建全局变量,不能用 with,
严格模式中 call apply 传入 null undefined 保持原样不被转换为 window
解析:一、概述除了正常运行模式,ECMAscript 5 添加了第二种运行模式:“严格模式”(strict mode)。顾名思义,这种模式使得 Javascript 在更严格的条件下运行。设立"严格模式"的目的,主要有以下几个:

消除 Javascript 语法的一些不合理、不严谨之处,减少一些怪异行为;

消除代码运行的一些不安全之处,保证代码运行的安全;

提高编译器效率,增加运行速度;

为未来新版本的 Javascript 做好铺垫。

"严格模式"体现了 Javascript 更合理、更安全、更严谨的发展方向,包括 IE 10 在内的主流浏览器,都已经支持它,许多大项目已经开始全面拥抱它。另一方面,同样的代码,在"严格模式"中,可能会有不一样的运行结果;一些在"正常模式"下可以运行的语句,在"严格模式"下将不能运行。掌握这些内容,有助于更细致深入地理解 Javascript,让你变成一个更好的程序员。本文将对"严格模式"做详细介绍。二、进入标志进入"严格模式"的标志,是下面这行语句:“use strict”;老版本的浏览器会把它当作一行普通字符串,加以忽略。三、如何调用"严格模式"有两种调用方法,适用于不同的场合。3.1 针对整个脚本文件将"use strict"放在脚本文件的第一行,则整个脚本都将以"严格模式"运行。如果这行语句不在第一行,则无效,整个脚本以"正常模式"运行。如果不同模式的代码文件合并成一个文件,这一点需要特别注意。(严格地说,只要前面不是产生实际运行结果的语句,"use strict"可以不在第一行,比如直接跟在一个空的分号后面。)  ‹script›
    “use strict”;
    console.log(“这是严格模式。”);
  ‹/script›

‹script›
    console.log(“这是正常模式。”);kly, it’s almost 2 years ago now. I can admit it now - I run it on my school’s network that has about 50 computers.
  ‹/script›上面的代码表示,一个网页中依次有两段 Javascript 代码。前一个 script 标签是严格模式,后一个不是。3.2 针对单个函数将"use strict"放在函数体的第一行,则整个函数以"严格模式"运行。function strict() {
“use strict”;
return “这是严格模式。”;
}

function notStrict() {
return “这是正常模式。”;
}3.3 脚本文件的变通写法因为第一种调用方法不利于文件合并,所以更好的做法是,借用第二种方法,将整个脚本文件放在一个立即执行的匿名函数之中。(function() {
“use strict”; // some code here

})();四、语法和行为改变严格模式对 Javascript 的语法和行为,都做了一些改变。4.1 全局变量显式声明在正常模式中,如果一个变量没有声明就赋值,默认是全局变量。严格模式禁止这种用法,全局变量必须显式声明。“use strict”;

v = 1; // 报错,v未声明

for (i = 0; i ‹ 2; i++) {
// 报错,i未声明
}因此,严格模式下,变量都必须先用 var 命令声明,然后再使用。4.2 静态绑定Javascript 语言的一个特点,就是允许"动态绑定",即某些属性和方法到底属于哪一个对象,不是在编译时确定的,而是在运行时(runtime)确定的。严格模式对动态绑定做了一些限制。某些情况下,只允许静态绑定。也就是说,属性和方法到底归属哪个对象,在编译阶段就确定。这样做有利于编译效率的提高,也使得代码更容易阅读,更少出现意外。具体来说,涉及以下几个方面。(1)禁止使用 with 语句因为 with 语句无法在编译时就确定,属性到底归属哪个对象。  “use strict”;

var v = 1;

with (o){ // 语法错误
    v = 2;
  }(2)创设 eval 作用域正常模式下,Javascript 语言有两种变量作用域(scope):全局作用域和函数作用域。严格模式创设了第三种作用域:eval 作用域。正常模式下,eval 语句的作用域,取决于它处于全局作用域,还是处于函数作用域。严格模式下,eval 语句本身就是一个作用域,不再能够生成全局变量了,它所生成的变量只能用于 eval 内部。“use strict”;

var x = 2;

console.info(eval(“var x = 5; x”)); // 5

console.info(x); // 24.3 增强的安全措施(1)禁止 this 关键字指向全局对象function f() {
return !this;
} // 返回false,因为"this"指向全局对象,"!this"就是false
function f() {
“use strict”;
return !this;
} // 返回true,因为严格模式下,this的值为undefined,所以"!this"为true。因此,使用构造函数时,如果忘了加 new,this 不再指向全局对象,而是报错。function f() {
“use strict”;

this.a = 1;
}

f(); // 报错,this未定义(2)禁止在函数内部遍历调用栈function f1() {
“use strict”;

f1.caller; // 报错

f1.arguments; // 报错
}

f1();4.4 禁止删除变量严格模式下无法删除变量。只有 configurable 设置为 true 的对象属性,才能被删除。  “use strict”;

var x;

delete x; // 语法错误

var o = Object.create(null, {‘x’: {
      value: 1,
      configurable: true
  }});

delete o.x; // 删除成功4.5 显式报错正常模式下,对一个对象的只读属性进行赋值,不会报错,只会默默地失败。严格模式下,将报错。“use strict”;

var o = {};

Object.defineProperty(o, “v”, { value: 1, writable: false });

o.v = 2; // 报错严格模式下,对一个使用 getter 方法读取的属性进行赋值,会报错。“use strict”;

var o = {
get v() {
return 1;
}
};

o.v = 2; // 报错严格模式下,对禁止扩展的对象添加新属性,会报错。“use strict”;

var o = {};

Object.preventExtensions(o);

o.v = 1; // 报错严格模式下,删除一个不可删除的属性,会报错。“use strict”;

delete Object.prototype; // 报错4.6 重名错误严格模式新增了一些语法错误。(1)对象不能有重名的属性正常模式下,如果对象有多个重名属性,最后赋值的那个属性会覆盖前面的值。严格模式下,这属于语法错误。“use strict”;

var o = {
p: 1,
p: 2
}; // 语法错误(2)函数不能有重名的参数正常模式下,如果函数有多个重名的参数,可以用 arguments[i]读取。严格模式下,这属于语法错误。  “use strict”;

function f(a, a, b) { // 语法错误

return ;

}4.7 禁止八进制表示法正常模式下,整数的第一位如果是 0,表示这是八进制数,比如 0100 等于十进制的 64。严格模式禁止这种表示法,整数第一位为 0,将报错。  “use strict”;

var n = 0100; // 语法错误4.8 arguments 对象的限制arguments 是函数的参数对象,严格模式对它的使用做了限制。(1)不允许对 arguments 赋值  “use strict”;

arguments++; // 语法错误

var obj = { set p(arguments) { } }; // 语法错误

try { } catch (arguments) { } // 语法错误

function arguments() { } // 语法错误

var f = new Function(“arguments”, “‘use strict’; return 17;”); // 语法错误(2)arguments 不再追踪参数的变化function f(a) {
a = 2;

return [a, arguments[0]];
}

f(1); // 正常模式为[2,2]

function f(a) {
“use strict”;
29.JS 严格模式和正常模式
严格模式使用"use strict";作用:
消除 Javascript 语法的一些不合理、不严谨之处,减少一些怪异行为;
消除代码运行的一些不安全之处,保证代码运行的安全;
提高编译器效率,增加运行速度;
为未来新版本的 Javascript 做好铺垫。
表现:
严格模式下, delete 运算符后跟随非法标识符(即 delete 不存在的标识符),会抛出语法错误; 非严格模式下,会静默失败并返回 false
严格模式中,对象直接量中定义同名属性会抛出语法错误; 非严格模式不会报错
严格模式中,函数形参存在同名的,抛出错误; 非严格模式不会
严格模式不允许八进制整数直接量(如:023)
严格模式中,arguments 对象是传入函数内实参列表的静态副本;非严格模式下,arguments 对象里的元素和对应的实参是指向同一个值的引用
严格模式中 eval 和 arguments 当做关键字,它们不能被赋值和用作变量声明
严格模式会限制对调用栈的检测能力,访问 arguments.callee.caller 会抛出异常
严格模式 变量必须先声明,直接给变量赋值,不会隐式创建全局变量,不能用 with,
严格模式中 call apply 传入 null undefined 保持原样不被转换为 window
解析:一、概述除了正常运行模式,ECMAscript 5 添加了第二种运行模式:“严格模式”(strict mode)。顾名思义,这种模式使得 Javascript 在更严格的条件下运行。设立"严格模式"的目的,主要有以下几个:

消除 Javascript 语法的一些不合理、不严谨之处,减少一些怪异行为;

消除代码运行的一些不安全之处,保证代码运行的安全;

提高编译器效率,增加运行速度;

为未来新版本的 Javascript 做好铺垫。

"严格模式"体现了 Javascript 更合理、更安全、更严谨的发展方向,包括 IE 10 在内的主流浏览器,都已经支持它,许多大项目已经开始全面拥抱它。另一方面,同样的代码,在"严格模式"中,可能会有不一样的运行结果;一些在"正常模式"下可以运行的语句,在"严格模式"下将不能运行。掌握这些内容,有助于更细致深入地理解 Javascript,让你变成一个更好的程序员。本文将对"严格模式

30.移动端 click 事件、touch 事件、tap 事件的区别

click 事件在移动端会有 200-300ms ms 的延迟,主要原因是苹果手机在设计时,考虑到用户在浏览网页时需要放大,所以,在用户点击的 200-300ms 之后,才触发 click,如果 200-300ms 之内还有 click,就会进行放大缩小。
touch 事件是针对触屏手机上的触摸事件。现今大多数触屏手机 webkit 内核提供了 touch 事件的监听,让开发者可以获取用户触摸屏幕时的一些信息。其中包括:touchstart,touchmove,touchend,touchcancel 这四个事件,touchstart touchmove touchend 事件可以类比于 mousedown mouseover mouseup 的触发
tap 事件在移动端,代替 click 作为点击事件,tap 事件被很多框架(如 zepto)封装,来减少这延迟问题, tap 事件不是原生的,所以是封装的,那么具体是如何实现的呢?

‹script›
function tap(ele, callback) {
// 记录开始时间
var startTime = 0,
// 控制允许延迟的时间
delayTime = 200,
// 记录是否移动,如果移动,则不触发tap事件
isMove = false;

// 在touchstart时记录开始的时间
ele.addEventListener(‘touchstart’, function (e) {
startTime = Date.now();
});

// 如果touchmove事件被触发,则isMove为true
ele.addEventListener(‘touchmove’, function (e) {
isMove = true;
});

// 如果touchmove事件触发或者中间时间超过了延迟时间,则返回,否则,调用回调函数。
ele.addEventListener(‘touchend’, function (e) {
if (isMove || (Date.now() - startTime › delayTime)) {
return;
} else {
callback(e);
}
})
}

var btn = document.getElementById(‘btn’);
tap(btn, function () {
alert(‘taped’);
});
‹/script›拓展:点透问题如果我们在移动端所有的 click 都替换为了 tap 事件,还是会触发点透问题的,因为实质是: 在同一个 z 轴上,z-index 不同的两个元素,上面的元素是一个绑定了 tap 事件的,下面是一个 a 标签,一旦 tap 触发,这个元素就会 display: none,而从上面的 tap 可以看出,有 touchstart、touchend,所以会 300ms 之后触发 click 事件,而 z-index 已经消失了,所以,触发了下面的 a 的 click 事件,注意: 我们认为 a 标签默认是绑定了 click 事件的。而这种现象不是我们所期待的。解决方案: (1)使用 fastclick。 (2)添加一个延迟。(1)直接引入 fastclick 库。window.addEventListener(
“load”,
function() {
FastClick.attach(document.body);
},
false
);这样,就可以成功解决问题了。(2)对于上一个 tap 做延迟。tap(ele, function() {
setTimeout(function() {
ele.style.display = “none”;
}, 300);
});这样,过了 300ms,那么 click 事件就不会触发在下面的 a 标签上了。

31.JS 单线程还是多线程,如何显示异步操作

JS 本身是单线程的,他是依靠浏览器完成的异步操作。解析:具体步骤,1、主线程 执行 js 中所有的代码。2、主线程 在执行过程中发现了需要异步的任务任务后扔给浏览器(浏览器创建多个线程执行),并在 callback queque 中创建对应的回调函数(回调函数是一个对象,包含该函数是否执行完毕等)。3、主线程 已经执行完毕所有同步代码。开始监听 callback queque 一旦 浏览器 中某个线程任务完成将会改变回调函数的状态。主线程查看到某个函数的状态为已完成,就会执行该函数。

32. JavaScript 数组的函数 map/forEach/reduce/filter

map
// map
//作用:对数组进行遍历
//返回值:新的数组
// 是否改变:否
var arr = [2, 5, 3, 4];
var ret = arr.map(function(value) {
return value + 1;
});
console.log(ret); //[3,6,4,5]
console.log(arr); //[2,5,3,4]
forEach
// forEach 方法
// 作用:遍历数组的每一项
// 返回值:undefined
// 是否改变:否
var arr = [2, 5, 3, 4];
var ret = arr.forEach(function(value) {
console.log(value); // 2, 5, 3, 4
});
console.log(ret); //undefined
console.log(arr); //[2,5,3,4]
reduce
// reduce 方法
// 作用:对数组进行迭代,然后两两进行操作,最后返回一个值
// 返回值:return出来的结果
// 是否改变:不会
var arr = [1, 2, 3, 4];
var ret = arr.reduce(function(a, b) {
return a * b;
});
console.log(ret); // 24
console.log(arr); // [1, 2, 3, 4]
filter
// filter 过滤
// 作用: 筛选一部分元素
// 返回值: 一个满足筛选条件的新数组
// 是否改变原有数组:不会

var arr = [2, 5, 3, 4];
var ret = arr.filter(function(value) {
return value › 3;
});
console.log(ret); //[5,4]
console.log(arr); //[2,5,3,4]

33. JS 块级作用域、变量提升

块级作用域
JS 中作用域有:全局作用域、函数作用域。没有块作用域的概念。ECMAScript 6(简称 ES6)中新增了块级作用域。块作用域由 { } 包括,if 语句和 for 语句里面的{ }也属于块作用域。
变量提升
如果变量声明在函数里面,则将变量声明提升到函数的开头
如果变量声明是一个全局变量,则将变量声明提升到全局作用域的开头
解析:‹script type=“text/javascript”›
{
var a = 1;
console.log(a); // 1
}
console.log(a); // 1
// 可见,通过var定义的变量可以跨块作用域访问到。

(function A() {
var b = 2;
console.log(b); // 2
})();
// console.log(b); // 报错,
// 可见,通过var定义的变量不能跨函数作用域访问到

if(true) {
var c = 3;
}
console.log©; // 3
for(var i = 0; i ‹ 4; i++) {
var d = 5;
};
console.log(i); // 4 (循环结束i已经是4,所以此处i为4)
console.log(d); // 5
// if语句和for语句中用var定义的变量可以在外面访问到,
// 可见,if语句和for语句属于块作用域,不属于函数作用域。

{
var a = 1;
let b = 2;
const c = 3;

{
	console.log(a);		// 1	子作用域可以访问到父作用域的变量
	console.log(b);		// 2	子作用域可以访问到父作用域的变量
	console.log(c);		// 3	子作用域可以访问到父作用域的变量

	var aa = 11;
	let bb = 22;
	const cc = 33;
}

console.log(aa);	// 11	// 可以跨块访问到子 块作用域 的变量
// console.log(bb);	// 报错	bb is not defined
// console.log(cc);	// 报错	cc is not defined

}
‹/script›拓展:var、let、const 的区别
var 定义的变量,没有块的概念,可以跨块访问, 不能跨函数访问。
let 定义的变量,只能在块作用域里访问,不能跨块访问,也不能跨函数访问。
const 用来定义常量,使用时必须初始化(即必须赋值),只能在块作用域里访问,而且不能修改。
同一个变量只能使用一种方式声明,不然会报错
‹script type=“text/javascript”›
// 块作用域
{
var a = 1;
let b = 2;
const c = 3;
// c = 4; // 报错

// let a = 'a';	// 报错  注:是上面 var a = 1; 那行报错
// var b = 'b';	// 报错:本行报错
// const a = 'a1';	// 报错  注:是上面 var a = 1; 那行报错
// let c = 'c';	// 报错:本行报错

var aa;
let bb;
// const cc; // 报错
console.log(a); // 1
console.log(b); // 2
console.log(c); // 3
console.log(aa); // undefined
console.log(bb); // undefined

}
console.log(a); // 1
// console.log(b); // 报错
// console.log©; // 报错

// 函数作用域
(function A() {
var d = 5;
let e = 6;
const f = 7;
console.log(d); // 5
console.log(e); // 6 (在同一个{ }中,也属于同一个块,可以正常访问到)
console.log(f); // 7 (在同一个{ }中,也属于同一个块,可以正常访问到)
})();
// console.log(d); // 报错
// console.log(e); // 报错
// console.log(f); // 报错
‹/script›

34. null/undefined 的区别

null: Null 类型,代表“空值",代表一个空对象指针,使用 typeof 运算得到 “object",所以你可以认为它是一个特殊的对象值。undefined: Undefined 类型,当一个声明了一个变量未初始化时,得到的就是 undefined。

我的手机 2020/4/6 20:50:20

35. JS 哪些操作会造成内存泄露

1)意外的全局变量引起的内存泄露function leak() {
leak = “xxx”; //leak成为一个全局变量,不会被回收
}2)闭包引起的内存泄露function bindEvent() {
var obj = document.createElement(“XXX”);
obj.οnclick = function() {
//Even if it’s a empty function
};
}闭包可以维持函数内局部变量,使其得不到释放。 上例定义事件回调时,由于是函数内定义函数,并且内部函数–事件回调的引用外暴了,形成了闭包。
解决之道,将事件处理函数定义在外部,解除闭包,或者在定义事件处理函数的外部函数中,删除对 dom 的引用。//将事件处理函数定义在外部
function onclickHandler() {
//do something
}
function bindEvent() {
var obj = document.createElement(“XXX”);
obj.οnclick = onclickHandler;
}

//在定义事件处理函数的外部函数中,删除对dom的引用
function bindEvent() {
var obj = document.createElement(“XXX”);
obj.οnclick = function() {
//Even if it’s a empty function
};
obj = null;
}3)没有清理的 DOM 元素引用var elements={
button: document.getElementById(“button”),
image: document.getElementById(“image”),
text: document.getElementById(“text”)
};
function doStuff(){
image.src=“http://some.url/image”;
button.click():
console.log(text.innerHTML)
}
function removeButton(){
document.body.removeChild(document.getElementById(‘button’))
}4)被遗忘的定时器或者回调var someResouce = getData();
setInterval(function() {
var node = document.getElementById(“Node”);
if (node) {
node.innerHTML = JSON.stringify(someResouce);
}
}, 1000);这样的代码很常见, 如果 id 为 Node 的元素从 DOM 中移除, 该定时器仍会存在, 同时, 因为回调函数中包含对 someResource 的引用, 定时器外面的 someResource 也不会被释放。5)子元素存在引起的内存泄露黄色是指直接被 js 变量所引用,在内存里,红色是指间接被 js 变量所引用,如上图,refB 被 refA 间接引用,导致即使 refB 变量被清空,也是不会被回收的子元素 refB 由于 parentNode 的间接引用,只要它不被删除,它所有的父元素(图中红色部分)都不会被删除。6)IE7/8 引用计数使用循环引用产生的问题function fn() {
var a = {};
var b = {};
a.pro = b;
b.pro = a;
}
fn();fn()执行完毕后,两个对象都已经离开环境,在标记清除方式下是没有问题的,但是在引用计数策略下,因为 a 和 b 的引用次数不为 0,所以不会被垃圾回收器回收内存,如果 fn 函数被大量调用,就会造成内存泄漏。在 IE7 与 IE8 上,内存直线上升。
IE 中有一部分对象并不是原生 js 对象。例如,其内存泄漏 DOM 和 BOM 中的对象就是使用 C++以 COM 对象的形式实现的,而 COM 对象的垃圾回收机制采用的就是引用计数策略。因此,即使 IE 的 js 引擎采用标记清除策略来实现,但 js 访问的 COM 对象依然是基于引用计数策略的。换句话说,只要在 IE 中涉及 COM 对象,就会存在循环引用的问题。var element = document.getElementById(“some_element”);
var myObject = new Object();
myObject.e = element;
element.o = myObject;上面的例子在一个 DOM 元素(element)与一个原生 js 对象(myObject)之间创建了循环引用。其中,变量 myObject 有一个名为 e 的属性指向 element 对象;而变量 element 也有一个属性名为 o 回指 myObject。由于存在这个循环引用,即使例子中的 DOM 从页面中移除,它也永远不会被回收。看上面的例子,有人会觉得太弱了,谁会做这样无聊的事情,但是其实我们经常会这样做window.οnlοad=function outerFunction(){
var obj=document.getElementById(“element”):
obj.οnclick=function innerFunction(){};
};这段代码看起来没什么问题,但是 obj 引用了 document.getElementById(“element”),而 document.getElementById(“element”)的 onclick 方法会引用外部环境中的变量,自然也包括 obj,是不是很隐蔽啊。最简单的解决方式就是自己手工解除循环引用,比如刚才的函数可以这样myObject.element=null;
element.o=null;
window.οnlοad=function outerFunction(){
var obj=document.getElementById(“element”):
obj.οnclick=function innerFunction(){};
obj=null;
};将变量设置为 null 意味着切断变量与它此前引用的值之间的连接。当垃圾回收器下次运行时,就会删除这些值并回收它们占用的内存。 要注意的是,IE9+并不存在循环引用导致 Dom 内存泄漏问题,可能是微软做了优化,或者 Dom 的回收方式已经改变解析:1、JS 的回收机制JavaScript 垃圾回收的机制很简单:找出不再使用的变量,然后释放掉其占用的内存,但是这个过程不是实时的,因为其开销比较大,所以垃圾回收系统(GC)会按照固定的时间间隔,周期性的执行。到底哪个变量是没有用的?所以垃圾收集器必须跟踪到底哪个变量没用,对于不再有用的变量打上标记,以备将来收回其内存。用于标记的无用变量的策略可能因实现而有所区别,通常情况下有两种实现方式:标记清除和引用计数。引用计数不太常用,标记清除较为常用。2、标记清除(mark and sweep)js 中最常用的垃圾回收方式就是标记清除。当变量进入环境时,例如,在函数中声明一个变量,就将这个变量标记为“进入环境”。从逻辑上讲,永远不能释放进入环境的变量所占用的内存,因为只要执行流进入相应的环境,就可能会用到它们。而当变量离开环境时,则将其标记为“离开环境”。function test() {
var a = 10; //被标记,进入环境
var b = 20; //被标记,进入环境
}
test(); //执行完毕之后a、b又被标记离开环境,被回收3、引用计数(reference counting)引用计数的含义是跟踪记录每个值被引用的次数。当声明了一个变量并将一个引用类型值(function object array)赋给该变量时,则这个值的引用次数就是 1。如果同一个值又被赋给另一个变量,则该值的引用次数加 1。相反,如果包含对这个值引用的变量又取得了另外一个值,则这个值的引用次数减 1。当这个值的引用次数变成 0 时,则说明没有办法再访问这个值了,因而就可以将其占用的内存空间回收回来。这样,当垃圾回收器下次再运行时,它就会释放那些引用次数为 0 的值所占用的内存。function test() {
var a = {}; //a的引用次数为0
var b = a; //a的引用次数加1,为1
var c = a; //a的引用次数加1,为2
var b = {}; //a的引用次数减1,为1
}4、如何分析内存的使用情况Google Chrome 浏览器提供了非常强大的 JS 调试工具,Memory 视图 profiles 视图让你可以对 JavaScript 代码运行时的内存进行快照,并且可以比较这些内存快照。它还让你可以记录一段时间内的内存分配情况。在每一个结果视图中都可以展示不同类型的列表,但是对我们最有用的是 summary 列表和 comparison 列表。 summary 视图提供了不同类型的分配对象以及它们的合计大小:shallow size (一个特定类型的所有对象的总和)和 retained size (shallow size 加上保留此对象的其它对象的大小)。distance 显示了对象到达 GC 根(校者注:最初引用的那块内存,具体内容可自行搜索该术语)的最短距离。 comparison 视图提供了同样的信息但是允许对比不同的快照。这对于找到泄漏很有帮助。5、怎样避免内存泄露1)减少不必要的全局变量,或者生命周期较长的对象,及时对无用的数据进行垃圾回收;2)注意程序逻辑,避免“死循环”之类的 ;3)避免创建过多的对象 原则:不用了的东西要及时归还。参考

36.重排与重绘的区别,什么情况下会触发?

简述重排的概念
浏览器下载完页面中的所有组件(HTML、JavaScript、CSS、图片)之后会解析生成两个内部数据结构(DOM 树和渲染树),DOM 树表示页面结构,渲染树表示 DOM 节点如何显示。重排是 DOM 元素的几何属性变化,DOM 树的结构变化,渲染树需要重新计算。

简述重绘的概念
重绘是一个元素外观的改变所触发的浏览器行为,例如改变 visibility、outline、背景色等属性。浏览器会根据元素的新属性重新绘制,使元素呈现新的外观。由于浏览器的流布局,对渲染树的计算通常只需要遍历一次就可以完成。但 table 及其内部元素除外,它可能需要多次计算才能确定好其在渲染树中节点的属性值,比同等元素要多花两倍时间,这就是我们尽量避免使用 table 布局页面的原因之一。
简述重绘和重排的关系
重绘不会引起重排,但重排一定会引起重绘,一个元素的重排通常会带来一系列的反应,甚至触发整个文档的重排和重绘,性能代价是高昂的。
什么情况下会触发重排?
页面渲染初始化时;(这个无法避免)
浏览器窗口改变尺寸;
元素尺寸改变时;
元素位置改变时;
元素内容改变时;
添加或删除可见的 DOM 元素时。

重排优化有如下五种方法

将多次改变样式属性的操作合并成一次操作,减少 DOM 访问。
如果要批量添加 DOM,可以先让元素脱离文档流,操作完后再带入文档流,这样只会触发一次重排。(fragment 元素的应用)
将需要多次重排的元素,position 属性设为 absolute 或 fixed,这样此元素就脱离了文档流,它的变化不会影响到其他元素。例如有动画效果的元素就最好设置为绝对定位。
由于 display 属性为 none 的元素不在渲染树中,对隐藏的元素操作不会引发其他元素的重排。如果要对一个元素进行复杂的操作时,可以先隐藏它,操作完成后再显示。这样只在隐藏和显示时触发两次重排。
在内存中多次操作节点,完成后再添加到文档中去。例如要异步获取表格数据,渲染到页面。可以先取得数据后在内存中构建整个表格的 html 片段,再一次性添加到文档中去,而不是循环添加每一行。

37.发布订阅设计模式

发布/订阅模式(Publish Subscribe Pattern)属于设计模式中的行为(Behavioral Patterns)解析:

  1. jsonp 优缺点?
    jsonp 优缺点
    1.优点

1.1 它不像 XMLHttpRequest 对象实现的 Ajax 请求那样受到同源策略的限制,JSONP 可以跨越同源策略;
1.2 它的兼容性更好,在更加古老的浏览器中都可以运行,不需要 XMLHttpRequest 或 ActiveX 的支持
1.3 在请求完毕后可以通过调用 callback 的方式回传结果。将回调方法的权限给了调用方。这个就相当于将 controller 层和 view 层终于分 开了。我提供的 jsonp 服务只提供纯服务的数据,至于提供服务以 后的页面渲染和后续 view 操作都由调用者来自己定义就好了。如果有两个页面需要渲染同一份数据,你们只需要有不同的渲染逻辑就可以了,逻辑都可以使用同 一个 jsonp 服务。

2.缺点

2.1 它只支持 GET 请求而不支持 POST 等其它类型的 HTTP 请求
2.2 它只支持跨域 HTTP 请求这种情况,不能解决不同域的两个页面之间如何进行 JavaScript 调用的问题。
2.3 jsonp 在调用失败的时候不会返回各种 HTTP 状态码。
2.4 缺点是安全性。万一假如提供 jsonp 的服务存在页面注入漏洞,即它返回的 javascript 的内容被人控制的。那么结果是什么?所有调用这个 jsonp 的网站都会存在漏洞。于是无法把危险控制在一个域名下…所以在使用 jsonp 的时候必须要保证使用的 jsonp 服务必须是安全可信的

39.兼容各种浏览器版本的事件绑定

/*兼容低版本IE,ele为需要绑定事件的元素,
eventName为事件名(保持addEventListener语法,去掉on),fun为事件响应函数
*/

function addEvent(ele, eventName, fun) {
if (ele.addEventListener) {
ele.addEventListener(eventName, fun, false);
} else {
ele.attachEvent(“on” + eventNme, fun);
}
}

40.typescript 遇到过什么坑

main.ts 报错( Cannot find module ‘./App.vue’.)原因: typescript 不能识别.vue 文件解决办法: 引入 vue 的 typescript declare 库

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值