一、选择题
(多选题)1、下列说法正确的是(A、B、D)
A、样式文件的加载会阻塞脚本的执行
B、iframe 会阻塞主页面的 load 事件
C、<audio>、<img>
中的资源下载会阻塞页面解析
D、页面文档完全加载并解析完毕之后会触发 DOMContentLoaded 事件
解析:
- css加载不会阻塞DOM树的解析
- css加载会阻塞DOM树的渲染
- css加载会阻塞后面js语句的执行
- iframe会阻塞主页面的onload事件;
- 搜索引擎检索程序无法解读这种页面,不利于SEO;
- 会影响页面的并行加载。
- 并行加载:同一时间对同一域名下的加载数量是有限制的:
- MDN解析:当初始HTML文档已完全加载和解析时,将触发DOMContentLoaded事件,而不需要等待样式表,图像和子框架页面加载(事件可以用来检测HTML页面是否完全加载完毕(fully-loaded))。
- 会阻塞dom解析的资源有:
1.内联css
2.内联js
3.普通外联js
4.外联defer js
5.js之前的外联css
(多选题)2、下列说法错误的是(A、B)
A、当前域的 cookie 都可以通过 js 在当前域下获取
B、JSONP 是 XMLHttpRequest 中的一种
C、同源策略是浏览器的安全策略
D、localStorage API 不支持设置过期时间
解析:
- 对于一些保密性较高的 cookie 后端可以通过设置 HttpOnly 标记 cookie 只能通过 http 传递,前端 js 无法读取,这样可以防范xss攻击。
- JSONP 用的是 html 的 script 标签,xmlhttprequest 是浏览器内置的 HTTP 相关对象,两者不同。
- JSONP 是一种无需考虑跨域问题即可传送 JSON 数据的方法。
- JSONP 不使用 XMLHttpRequest 对象。
- JSONP 使用
<script>
标签取而代之。 - 前端常见跨域解决方案(全)
(多选题)3、下列说法正确的是(A、C、D)
A、requestAnimationFrame(foo) 确保使浏览器在下一次重绘之前调用 foo 方法
B、在 addEventListener 的处理方法中使用 e.preventDefault() 可以阻止事件冒泡
C、 把 <script>
标签的引入放在文档末尾可以确保脚本下载和执行均在文档解析完成后发生
D、多个 <script>
标签使用 defer 属性引入脚本时,可以确保脚本的执行是按照其被引入的顺序的
解析:
window.requestAnimationFrame()
方法告诉浏览器您希望执行动画并请求浏览器在下一次重绘之前调用指定的函数来更新动画。该方法使用一个回调函数作为参数,这个回调函数会在浏览器重绘之前调用。e.stopPropagation()
,阻止事件冒泡- 所有
<script>
标签引进的 JavaScript 会按照他们引入的顺序依次被解析,在没有使用 defer 或者 async 的情况下,只有在解析完前面<script>
元素中的代码之后,才会开始解析后面<script>
元素中的代码。由于浏览器会先解析完不使用 defer 属性的<script>
元素中的代码,然后再解析后面的内容,所以一般应该把<script>
元素放在页面最后,即主要内容后面,</body>
标签前面。 - 使用
defer
属性可以让脚本在文档完全呈现之后再执行,延迟脚本总是按照它们指定的顺序执行。 - 使用
async
属性可以表示当前脚本不必等待其他脚本,也不必阻塞文档呈现。不能保证异步脚本按照它们在页面中出现的顺序执行。
(多选题)4、下列说法正确的是(B、C)
A、setTimeout(foo, 0) 这行代码可以等价替换为 foo()
B、使用 Object.assign(obj1, obj2) 可以实现对象的浅拷贝
C、for…in 循环只能遍历对象上的可枚举属性
D、Object.keys() 可以遍历出对象原型链上的属性
解析:
- ES5 引入了 Object.keys 方法,成员是参数对象自身的(不含继承的)所有可遍历( enumerable )属性的键名。
- for…in 循环对象的所有枚举属性(包括原型上),然后再使用 hasOwnProperty() 方法来忽略继承属性。
- 获取实例对象obj的原型对象,有三种方法
- obj.proto
- obj.constructor.prototype
- Object.getPrototypeOf(obj)
上面三种方法之中,前两种都不是很可靠。
最新的ES6标准规定,__proto__属性只有浏览器才需要部署,其他环境可以不部署。而 obj.constructor.prototype 在手动改变原型对象时,可能会失效。
二、问答题
1、请根据下面的示例描述原型链与继承的关系并解释原因:
class A {}
class B extends A {}
const a = new A()
const b = new B()
a.proto ===
b.proto ===
B.proto ===
B.prototype.proto ===
b.proto.proto ===
答案:
A.prototype
B.prototype
A
A.prototype
A.prototype
继承关系图:
这里 class B extends A,那么关于原型 class 之间的原型继承可得出上面这些等式。
用关系图来表达上面的这些等式会更容易理解。
2、请表述以下代码的执行结果和原因:
setTimeout(function () {
console.log(1);
}, 0);
new Promise(function execulor(resolve) {
console.log(2);
for (var i = 0; i < 10000; i += 1) {
i == 9999 && resolve()
}
console.log(3);
}).then(function () {
console.log(4);
})
console.log(5);
答案:
结果:
2
3
5
4
1
原因:
1. setTimeout 是定时器,属于宏任务,会在本轮宏任务结束之后的下一轮宏任务出现,现在进入宏任务队列等待 。
2. Promise 执行,打印 2 。
3. i为9999时,执行resolve,promise的状态变为resolve,然后执行下一行代码,打印 3。
4. promise 的状态为resolved,所以 then 语句进入微任务队列中,在宏任务结束之前执行。
5. 打印 5。
6. 此时最后一行代码执行完毕,执行微任务,打印 4。
7. 进入下一轮宏任务,打印 1。
3、ajax 的 readyState 有哪几个状态,含义分别是什么?
答案:
5 个状态,分别是 0-4
0: 还没调用open方法
1: 未调用send方法,也就是还没发送数据给服务器
2: 还没有接收到响应
3: 开始接收到了部分数据
4: 接收完成,数据可以在客户端使用了
3、JavaScript 严格模式下有哪些不同?
答案:
1.严格模式对 JavaScript 的语法和行为,都做了一些改变。
严格模式下,变量必须先声明再使用,严禁删除已经声明变量。
严格模式下,预编译时 this 为 undefined。
严格模式下,不支持arguments、caller、callee、with。
严格模式下,在函数内部对修改参数不会反映到 arguments 中,淘汰了 arguments.callee 和 arguments.caller, 抛弃 with 语句。
严格模式下,不可在 if 内部声明函数
严格模式下,拒绝重复的属性和参数。
严格模式下,局部的this必须被赋值、赋值什么就是什么。
2. 严格模式下 this 指向问题
① 以前在全局作用域函数中的 this 指向 window 对象。
② 严格模式下全局作用域中函数中的 this 是 undefined。
③ 以前构造函数不加 new 也可以调用,当普通函数,this 指向全局对象。
④ 严格模式下,如果构造函数不加 new 调用, this 指向的是 undefined ,如果给他赋值则会报错 。
4、请给出下列代码的输出:
null == undefined
typeof NaN;
typeof Function;
typeof Object;
typeof {};
'a' + 1;
'a' - 1;
Function instanceof Object;
Object instanceof Function;
答案:
console.log(null == undefined); // true
console.log(typeof NaN); // 'number'
console.log(typeof Function); // 'function'
console.log(typeof Object); // 'function'
console.log(typeof {}); // 'object'
console.log('a' + 1); // 'a1'
console.log('a' - 1); // NaN
console.log(Function instanceof Object); // true
console.log(Object instanceof Function); // true
5、请描述打印结果并解释原因:
'use strict'
var name = 'Jay'
var person = {
name: 'Wang',
pro: {
name: 'Michael',
getName: function () {
return this.name;
}
}
}
console.log(person.pro.getName());
var pepole = person.pro.getName;
console.log(pepole());
答案:
Michael
throw Error
原因:
"use strict" 声明以严格模式执行
输出:“Michael”
解释:这里是person.pro调用了getName(), getName()里面的this指向了person.pro, 所以这里的this.name == "Michael"
输出:error报错
解释:将person.pro.getName方法赋给了pepole, 然后在全局执行上下文中调用了pepole(),因为是在严格模式下执行,所以pepole()里面的this是指向 undefined, undefined 又获取name 属性,最后导致报错。
三、编程题
1、使用 javascript 实现 paddingNum 方法:分割数字,每隔三位使用逗号分隔一次,参数是Number,返回分隔后的字符串。
例子如下:
paddingNum(2,3); // return '33'
paddingNum(1234.56); // return '1,234.56'
paddingNum(123456789); // return '123,456,789'
paddingNum(-987654.3); // return '-987,654.3'
代码:
function paddingNum(inputNum) {
let flag = inputNum > 0 ? true : false;
let numArr = Math.abs(inputNum).toString().split('.');
let right = numArr[1] ? '.' + numArr[1] : ''; // 小数点右边的数字
let left = numArr[0]; // 小数点左边的数字
let temp = '';
while (left.length > 3) {
// str.slice(start, end); 截取字符串,返回截取的字符串,end取不到
temp = ',' + left.slice(-3) + temp;
left = left.slice(0, left.length - 3);
}
return flag ? left + temp + right : '-' + left + temp + right;
}
console.log(paddingNum(2, 3)); // return '33'
console.log(paddingNum(1234.56)); // return '1,234.56'
console.log(paddingNum(123456789)); // return '123,456,789'
console.log(paddingNum(-987654.3)); // return '-987,654.3'
2、使用 javascript 实现 productExceptSelf 方法:给定长度为 n 的整数数组 nums,其中 n > 1,返回输出数组 output,其中 output[i] 等于 nums 中除 nums[i] 之外其余各元素的乘积,参数为一个整数数组,返回一个数组。
代码:
function productExceptSelf(nums) {
var total = 1;
//获得所有元素的乘积
nums.forEach(value => {
total *= value;
});
// map() 方法返回一个新数组,数组中的元素为原始数组元素调用函数处理后的值。
// map() 方法按照原始数组元素顺序依次处理元素。
//除以自身
var newNums = nums.map(value => {
return total / value;
});
return newNums;
}
var input = [1, 2, 3, 4];
console.log(productExceptSelf(input)); // [24, 12, 8, 6]
var input2 = [2, 3, 4, 5];
console.log(productExceptSelf(input2)); // [60, 40, 30, 24]
3、薯队长带着小红薯参加密室逃脱团建游戏,首先遇到了反转游戏,小红薯们根据游戏提示收集了多个单词线索,并将单词按要求加一个空格组 成了句子,最终要求把句子按单词反转解密。 说明:收集的时候单词前后可能会有多个空格,反转后单词不能有多个空格,具体见输入输出样例。
代码:
function reverseStr(str) {
// \s 匹配空格
var s = str.match(/[^\s]+/g)
s = s.reverse();
return s.join(' ');
}
str = 'Hello world ! ni hao ya';
console.log(reverseStr(str)); // ya hao ni ! world Hello
知识点:
match() 方法将检索字符串 String Object,以找到一个或多个与 regexp 匹配的文本。这个方法的行为在很大程度上有赖于 regexp 是否具有标志 g。如果 regexp 没有标志 g,那么 match() 方法就只能在 string Object 中执行一次匹配。如果没有找到任何匹配的文本, match() 将返回 null。否则,它将返回一个数组,其中存放了与它找到的匹配文本有关的信息。
4、使用 javascript 实现 filterSensitiveWord() 方法:敏感词过滤,将指定字符串中的敏感词替换为星号字符(*),参数有目标字符串input,敏感词列表sensitive,返回过滤后的字符串。
代码:
// for...of 用于遍历一个迭代器
function filterSensitiveWord(input, sensitive) {
for (let item of sensitive) {
let len = item.length;
let ret = new RegExp(`${item}`, 'g');
input = input.replace(ret, '*'.repeat(len));
}
return input;
}
let test = ['想要轻生, have sex, sexy baby', ['轻生', 'sex']];
console.log(filterSensitiveWord(test[0], test[1])); // 想要**, have ***, ***y baby
5、使用 javascript 实现 dupicatedWord() 方法:统计数组中字符出现的频次,返回一个 Object<String, Boolean>
对象,字符为 key,当出现两次以上将 value 置为 true,否则为 false。
代码:
function dupicatedWord(arr) {
let obj = {};
arr.forEach((value) => {
obj[value] = typeof obj[value] === 'undefined' ? false : true;
});
return obj;
}
let arr = ['a', 'a', 'b', 'b', 'c'];
console.log(dupicatedWord(arr)); // {a: true, b: true, c: false}