1.作用域的分类
块作用域、词法作用域、动态作用域解析:1 块作用域 花括号 {}2 词法作用域(js 属于词法作用域)
作用域只跟在何处被创建有关系,跟在何处被调用没有关系3 动态作用域
作用域只跟在何处被调用有关系,跟在何处被创建没有关系
时倾 8:23:13
2.js 属于哪种作用域
词法作用域(函数作用域)解析:// 块作用域
/*{
var num =123;
}
console.log(num);*/
// 如果js属于块作用域,那么在花括号外部就无法访问到花括号内部的声明的num变量。
// 如果js不属于块级作用域,那么花括号外部就能够访问到花括号内部声明的num变量
// 能够输出num变量,也就说明js不属于块级作用。
// 在ES6 之前的版本js是不存在块级作用域的。
//js属于词法作用域还是动态作用域
// js中函数可以帮我们去形成一个作用域
/* function fn(){
var num =123;
}
fn();
//在函数外界能否访问到num这样一个变量
console.log(num)*/ //Uncaught ReferenceError: num is not defined
// 如果函数能够生成一个作用域,那么在函数外界就无法访问到函数内部声明的变量。
// js中的函数能够生成一个作用。 函数作用域 。
// 词法作用域:作用的外界只跟作用域在何处创建有关系,跟作用域在何处被调用没有关系
var num = 123;
function f1() {
console.log(num); //
}
function f2() {
var num = 456;
f1(); //f1在f2被调用的时候会被执行 。
}
f2();
//如果js是词法作用域,那么就会输出f1被创建的时候外部的num变量 123
//如果js是动态作用域,那么f1执行的时候就会输出f1被调用时外部环境中的num 456
3.自执行函数?用于什么场景?好处?
自执行函数:
1、声明一个匿名函数
2、马上调用这个匿名函数。
作用:创建一个独立的作用域。好处:防止变量弥散到全局,以免各种 js 库冲突。隔离作用域避免污染,或者截断作用域链,避免闭包造成引用变量无法释放。利用立即执行特性,返回需要的业务函数或对象,避免每次通过条件判断来处理场景:一般用于框架、插件等场景
4.多个页面之间如何进行通信
有如下几个方式:
cookie
web worker
localeStorage 和 sessionStorage
5.css 动画和 js 动画的差异
代码复杂度,js 动画代码相对复杂一些
动画运行时,对动画的控制程度上,js 能够让动画,暂停,取消,终止,css 动画不能添加事件
动画性能看,js 动画多了一个 js 解析的过程,性能不如 css 动画好
6.如何做到修改 url 参数页面不刷新
HTML5 引入了 history.pushState() 和 history.replaceState() 方法,它们分别可以添加和修改历史记录条目。let stateObj = {
foo: "bar"
};
history.pushState(stateObj, "page 2", "bar.html");假设当前页面为 foo.html,执行上述代码后会变为 bar.html,点击浏览器后退,会变为 foo.html,但浏览器并不会刷新。
pushState() 需要三个参数: 一个状态对象, 一个标题 (目前被忽略), 和 (可选的) 一个 URL. 让我们来解释下这三个参数详细内容:
状态对象 — 状态对象 state 是一个 JavaScript 对象,通过 pushState () 创建新的历史记录条目。无论什么时候用户导航到新的状态,popstate 事件就会被触发,且该事件的 state 属性包含该历史记录条目状态对象的副本。
状态对象可以是能被序列化的任何东西。原因在于 Firefox 将状态对象保存在用户的磁盘上,以便在用户重启浏览器时使用,我们规定了状态对象在序列化表示后有 640k 的大小限制。如果你给 pushState() 方法传了一个序列化后大于 640k 的状态对象,该方法会抛出异常。如果你需要更大的空间,建议使用 sessionStorage 以及 localStorage.
标题 — Firefox 目前忽略这个参数,但未来可能会用到。传递一个空字符串在这里是安全的,而在将来这是不安全的。二选一的话,你可以为跳转的 state 传递一个短标题。
URL — 该参数定义了新的历史 URL 记录。注意,调用 pushState() 后浏览器并不会立即加载这个 URL,但可能会在稍后某些情况下加载这个 URL,比如在用户重新打开浏览器时。新 URL 不必须为绝对路径。如果新 URL 是相对路径,那么它将被作为相对于当前 URL 处理。新 URL 必须与当前 URL 同源,否则 pushState() 会抛出一个异常。该参数是可选的,缺省为当前 URL。
7.数组方法 pop() push() unshift() shift()
arr.pop() 从后面删除元素,只能是一个,返回值是删除的元素
arr.push() 从后面添加元素,返回值为添加完后的数组的长度
arr.unshift() 从前面添加元素, 返回值是添加完后的数组的长度
arr.shift() 从前面删除元素,只能删除一个 返回值是删除的元素
8.事件绑定与普通事件有什么区别
用普通事件添加相同事件,下面会覆盖上面的,而事件绑定不会
普通事件是针对非 dom 元素,事件绑定是针对 dom 元素的事件
9. IE 和 DOM 事件流的区别
1.事件流的区别IE 采用冒泡型事件 Netscape 使用捕获型事件 DOM 使用先捕获后冒泡型事件
示例:复制代码代码如下:‹body›
‹div›
‹button›点击这里‹/button›
‹/div›
‹/body›冒泡型事件模型: button-›div-›body (IE 事件流)捕获型事件模型: body-›div-›button (Netscape 事件流)DOM 事件模型: body-›div-›button-›button-›div-›body (先捕获后冒泡)2.事件侦听函数的区别IE 使用:[Object].attachEvent("name_of_event_handler", fnHandler); //绑定函数
[Object].detachEvent("name_of_event_handler", fnHandler); //移除绑定DOM 使用:[Object].addEventListener("name_of_event", fnHandler, bCapture); //绑定函数
[Object].removeEventListener("name_of_event", fnHandler, bCapture); //移除绑定bCapture 参数用于设置事件绑定的阶段,true 为捕获阶段,false 为冒泡阶段。
10. IE 和标准下有哪些兼容性的写法
var ev = ev || window.event;
document.documentElement.clientWidth || document.body.clientWidth;
var target = ev.srcElement || ev.target;
11.变量提升
变量提升A、js 代码执行的过程
1 变量提升
2 代码从上到下依次执行
var 关键字和 function 关键字声明的变量会进行变量提升B、变量提升发生的环境:发生在代码所处的当前作用域。
变量提升
1 var 关键字进行的变量提升,会把变量提前声明,但是不会提前赋值 。
2 function 关键字对变量进行变量提升,既会把变量提前声明,又会把变量提前赋值,也就是把整个函数体提升到代码的顶部
3 有一些代码是不会执行的但是仍旧会发生变量提升,规则适用于 1,2
3.1 return 之后的代码依旧会发生变量提升,规则适用于 1,2
3.2 代码报错之后的代码依旧会发生变量提升,规则适用于 1,2
3.3 break 之后的代码依旧会发生变量提升,规则适用于 1,2
4 有一些代码是不会执行但是仍旧会发生变量提升,但是规则要发生变化
4.1 if 判断语句 if 判断语句中 var 关键字以及 function 关键字声明的变量只会发生提前声明,不会发生提前赋值,也就是不会吧函数体整体提升到当前作用域顶部。规则跟 1,2 不适用
4.2 switch case 规则跟 1,2 不适用
4.3 do while 规则跟 1,2 不适用
4.4 try catch catch 中声明的变量只会发生提前声明,不会发生提前赋值。
Ps:在条件判断语句和 try catch 中的声明的变量不管是否能够执行,都只会发生提前
声明,不会发生提前赋值。
解析:// 如果一个变量声明了但是未赋值,那么输出这个变量就会输出 undefined
var num;
console.log(num);
// 如果一个变量没有声明也没有赋值,那么就会报一个错:
console.log(num); // 输出一个不存在的变量 Uncaught ReferenceError: num is not defined// var 关键字进行的变量提升
console.log(num);
var num = 123;
console.log(num);
var num = 456;
console.log(num);
// 变量提升之后的代码:
var num;
console.log(num);
num = 123;
console.log(num);
num = 456;
console.log(num);// function 关键字的变量提升
console.log(fn);
function fn() {
console.log(1);
}
// 变量提升之后的代码:
function fn() {
console.log(1);
}
console.log(fn); // 输出fn的函数体// 3.1 return 之后的代码依旧会发生变量提升 规则适用于1,2
function fn() {
console.log(num);
return;
var num = 123;
}
fn();
// 变量提升之后的代码:
function fn() {
var num;
console.log(num);
return;
num = 123;
}
fn(); // undefined
function fn() {
console.log(fo);
return;
function fo() {}
}
fn();
// 变量提升之后的代码:
function fn() {
function fo() {}
console.log(fo);
return;
}
fn(); //输出fo的函数体//3.2 代码报错之后的代码依旧会进行变量提升,规则适用于1,2
console.log(num);
xsasfgdsfqdfsdf; //报一个错
var num = 123;
console.log(num);
// 变量提升之后的代码:
var num;
console.log(num); //输出 undefined
dsagdsqghdwfh; // 报一个错误 ,错误之后的代码不会被执行
num = 123;
console.log(num);//function 关键字
console.log(fn);
sasgfdhwhsdqg;
function fn() {}
console.log(fn);
// 变量提升之后的代码:
function fn() {}
console.log(fn); // 输出 fn 的函数体
asdgsdgdfgfdg; // 报一个错误,报错之后的代码不会被执行
console.log(fn);//4 代码不执行,但是会进行变量提升,不过规则不适用于1,2
//4.1 if判断语句
console.log(num);
if (false) {
var num = 123;
}
console.log(num)
// 变量提升之后的代码:
var num;
console.log(num); //undefined
if (false) {
num = 123;
}
console.log(num) //undefined
console.log(fn);
if (false) {
function fn() {}
}
console.log(fn);
// 变量提升之后的代码:
var fn;
function fn;
console.log(fn) //undefined
if (false) {
function fn() {}
}
console.log(fn) //undefined
/*function fn//Uncaught SyntaxError: Unexpected end of input*/// try catch
try {
console.log(num);
} catch (e) {
var num = 123;
}
console.log(num);
var num;
try {
console.log(num); // undefined
} catch (e) {
num = 123;
}
console.log(num); // undefined
try {
console.log(fn);
} catch (e) {
function fn() {}
}
console.log(fn);
var fn;
try {
console.log(fn); // undefined
} catch (e) {
num = 123;
}
console.log(fn); // undefined对应面试题
12.如何阻止冒泡与默认行为
阻止冒泡行为:非 IE 浏览器 stopPropagation(),IE 浏览器 window.event.cancelBubble = true
阻止默认行为:非 IE 浏览器 preventDefault(),IE 浏览器 window.event.returnValue = false
解析:当需要阻止冒泡行为时,可以使用function stopBubble(e) {
//如果提供了事件对象,则这是一个非IE浏览器
if (e && e.stopPropagation)
//因此它支持W3C的stopPropagation()方法
e.stopPropagation();
//否则,我们需要使用IE的方式来取消事件冒泡
else window.event.cancelBubble = true;
}当需要阻止默认行为时,可以使用//阻止浏览器的默认行为
function stopDefault(e) {
//阻止默认浏览器动作(W3C)
if (e && e.preventDefault) e.preventDefault();
//IE中阻止函数器默认动作的方式
else window.event.returnValue = false;
return false;
}
13.js 中 this 闭包 作用域
this:指向调用上下文闭包:定义一个函数就开辟了一个局部作用域,整个 js 执行环境有一个全局作用域作用域:一个函数可以访问其他函数中的变量(闭包是一个受保护的变量空间)var f = (function fn() {
var name = 1;
return function () {
name++;
console.log(name)
}
})()
==›undefined 有疑问
时倾 8:25:29
75.javascript 的本地对象,内置对象和宿主对象
本地对象
ECMA-262 把本地对象(native object)定义为“独立于宿主环境的 ECMAScript 实现提供的对象"。简单来说,本地对象就是 ECMA-262 定义的类(引用类型)。它们包括:Object、Function、Array、String、Boolean、Number、Date、RegExp、Error、EvalError、RangeError、ReferenceError、SyntaxError、TypeError、URIError
内置对象
JS中内置了17个对象,常用的是Array对象、Date对象、正则表达式对象、string对象、Global对象
宿主对象
由ECMAScript实现的宿主环境提供的对象,可以理解为:浏览器提供的对象。所有的BOM和DOM都是宿主对象。
15.javascript 的同源策略
一段脚本只能读取来自于同一来源的窗口和文档的属性解析:同源策略:限制从一个源加载的文档或脚本如何与来自另一个源的资源进行交互。这是一个用于隔离潜在恶意文件的关键的安全机制。(来自 MDN 官方的解释)简单来说就是:一段脚本只能读取来自于同一来源的窗口和文档的属性,这里的同一来源指的是主机名、协议和端口号的组合
具体解释:(1)源包括三个部分:协议、域名、端口(http 协议的默认端口是 80)。如果有任何一个部分不同,则源不同,那就是跨域了。(2)限制:这个源的文档没有权利去操作另一个源的文档。这个限制体现在:(要记住)Cookie、LocalStorage 和 IndexDB 无法获取。无法获取和操作 DOM。不能发送 Ajax 请求。我们要注意,Ajax 只适合同源的通信。同源策略带来的麻烦:ajax 在不同域名下的请求无法实现,需要进行跨域操作
16.事件冒泡与事件捕获
事件冒泡:由最具体的元素(目标元素)向外传播到最不具体的元素事件捕获:由最不确定的元素到目标元素
17.foo = foo||bar ,这行代码是什么意思?为什么要这样写?
这种写法称为短路表达式解析:相当于var foo;
if (foo) {
foo = foo;
} else {
foo = bar;
}常用于函数参数的空判断
18.复杂数据类型如何转变为字符串
首先,会调用 valueOf 方法,如果方法的返回值是一个基本数据类型,就返回这个值
如果调用 valueOf 方法之后的返回值仍旧是一个复杂数据类型,就会调用该对象的 toString 方法
如果 toString 方法调用之后的返回值是一个基本数据类型,就返回这个值,
如果 toString 方法调用之后的返回值是一个复杂数据类型,就报一个错误。
19.javascript 中 this 的指向问题
全局环境、普通函数(非严格模式)指向 window
普通函数(严格模式)指向 undefined
函数作为对象方法及原型链指向的就是上一级的对象
构造函数指向构造的对象
DOM 事件中指向触发事件的元素
箭头函数...
解析:1、全局环境全局环境下,this 始终指向全局对象(window),无论是否严格模式;// 在浏览器中,全局对象为 window 对象:
console.log(this === window); // true
this.a = 37;
console.log(window.a); // 372、函数上下文调用2.1 普通函数普通函数内部的 this 分两种情况,严格模式和非严格模式。(1)非严格模式下,没有被上一级的对象所调用,this 默认指向全局对象 window。function f1() {
return this;
}
f1() === window; // true(2)严格模式下,this 指向 undefined。function f2() {
"use strict"; // 这里是严格模式
return this;
}
f2() === undefined; // true2.2 函数作为对象的方法(1)函数有被上一级的对象所调用,那么 this 指向的就是上一级的对象。(2)多层嵌套的对象,内部方法的 this 指向离被调用函数最近的对象(window 也是对象,其内部对象调用方法的 this 指向内部对象, 而非 window)。//方式1
var o = {
prop: 37,
f: function() {
return this.prop;
}
};
//当 o.f()被调用时,函数内的this将绑定到o对象。
console.log(o.f()); // logs 37
//方式2
var o = { prop: 37 };
function independent() {
return this.prop;
}
//函数f作为o的成员方法调用
o.f = independent;
console.log(o.f()); // logs 37
//方式3
//this 的绑定只受最靠近的成员引用的影响
o.b = { g: independent, prop: 42 };
console.log(o.b.g()); // 42特殊例子// 例子1
var o = {
a: 10,
b: {
// a:12,
fn: function() {
console.log(this.a); //undefined
console.log(this); //{fn: ƒ}
}
}
};
o.b.fn();
// 例子2
var o = {
a: 10,
b: {
a: 12,
fn: function() {
console.log(this.a); //undefined
console.log(this); //window
}
}
};
var j = o.b.fn;
j();
// this永远指向的是最后调用它的对象,也就是看它执行的时候是谁调用的,例子2中虽然函数fn是被对象b所引用,但是在将fn赋值给变量j的时候并没有执行所以最终指向的是window,这和例子1是不一样的,例子1是直接执行了fn2.3 原型链中的 this(1)如果该方法存在于一个对象的原型链上,那么 this 指向的是调用这个方法的对象,就像该方法在对象上一样。var o = {
f: function() {
return this.a + this.b;
}
};
var p = Object.create(o);
p.a = 1;
p.b = 4;
console.log(p.f()); // 5上述例子中,对象 p 没有属于它自己的 f 属性,它的 f 属性继承自它的原型。当执行 p.f()时,会查找 p 的原型链,找到 f 函数并执行。因为 f 是作为 p 的方法调用的,所以函数中的 this 指向 p。(2)相同的概念也适用于当函数在一个 getter 或者 setter 中被调用。用作 getter 或 setter 的函数都会把 this 绑定到设置或获取属性的对象。(3)call()和 apply()方法:当函数通过 Function 对象的原型中继承的方法 call() 和 apply() 方法调用时, 其函数内部的 this 值可绑定到 call() & apply() 方法指定的第一个对象上, 如果第一个参数不是对象,JavaScript 内部会尝试将其转换成对象然后指向它。function add(c, d) {
return this.a + this.b + c + d;
}
var o = { a: 1, b: 3 };
add.call(o, 5, 7); // 1 + 3 + 5 + 7 = 16
add.apply(o, [10, 20]); // 1 + 3 + 10 + 20 = 34
function tt() {
console.log(this);
}
// 第一个参数不是对象,JavaScript内部会尝试将其转换成对象然后指向它。
tt.call(5); // 内部转成 Number {[[PrimitiveValue]]: 5}
tt.call("asd"); // 内部转成 String {0: "a", 1: "s", 2: "d", length: 3, [[PrimitiveValue]]: "asd"}(4)bind()方法:由 ES5 引入, 在 Function 的原型链上, Function.prototype.bind。通过 bind 方法绑定后, 函数将被永远绑定在其第一个参数对象上, 而无论其在什么情况下被调用。function f() {
return this.a;
}
var g = f.bind({ a: "azerty" });
console.log(g()); // azerty
var o = { a: 37, f: f, g: g };
console.log(o.f(), o.g()); // 37, azerty2.4 构造函数中的 this当一个函数用作构造函数时(使用 new 关键字),它的 this 被绑定到正在构造的新对象。构造器返回的默认值是 this 所指的那个对象,也可以手动返回其他的对象。function C() {
this.a = 37;
}
var o = new C();
console.log(o.a); // 37
// 为什么this会指向o?首先new关键字会创建一个空的对象,然后会自动调用一个函数apply方法,将this指向这个空对象,这样的话函数内部的this就会被这个空的对象替代。
function C2() {
this.a = 37;
return { a: 38 }; // 手动设置返回{a:38}对象
}
o = new C2();
console.log(o.a); // 38特殊例子当 this 碰到 return 时// 例子1
function fn() {
this.user = "追梦子";
return {};
}
var a = new fn();
console.log(a.user); //undefined
// 例子2
function fn() {
this.user = "追梦子";
return function() {};
}
var a = new fn();
console.log(a.user); //undefined
// 例子3
function fn() {
this.user = "追梦子";
return 1;
}
var a = new fn();
console.log(a.user); //追梦子
// 例子4
function fn() {
this.user = "追梦子";
return undefined;
}
var a = new fn();
console.log(a.user); //追梦子
// 例子5
function fn() {
this.user = "追梦子";
return undefined;
}
var a = new fn();
console.log(a); //fn {user: "追梦子"}
// 例子6
// 虽然null也是对象,但是在这里this还是指向那个函数的实例,因为null比较特殊
function fn() {
this.user = "追梦子";
return null;
}
var a = new fn();
console.log(a.user); //追梦子
// 总结:如果返回值是一个对象,那么this指向的就是那个返回的对象,如果返回值不是一个对象那么this还是指向函数的实例。2.5 setTimeout & setInterval(1)对于延时函数内部的回调函数的 this 指向全局对象 window;(2)可以通过 bind()方法改变内部函数 this 指向。//默认情况下代码
function Person() {
this.age = 0;
setTimeout(function() {
console.log(this);
}, 3000);
}
var p = new Person(); //3秒后返回 window 对象
//通过bind绑定
function Person() {
this.age = 0;
setTimeout(
function() {
console.log(this);
}.bind(this),
3000
);
}
var p = new Person(); //3秒后返回构造函数新生成的对象 Person{...}3、在 DOM 事件中3.1 作为一个 DOM 事件处理函数当函数被用作事件处理函数时,它的 this 指向触发事件的元素(针对 addEventListener 事件)。// 被调用时,将关联的元素变成蓝色
function bluify(e) {
//this指向所点击元素