前言
这里仅摘录一些感觉比较有意义,且有难度的题目。
正文
1、在大数据量场景下,以下哪种js中字符串连接方式较为高效( )
a+=b
a = a+b
Array.join()
Array.push()
+的处理机制是:新建一个临时字符串,将新字符串赋值为a+b,然后返回这个临新字符串并同时销毁原始字符串,所以字符串连接效率较低。所以用Array.join()不会新建临时字符串效率更高。 (当然以上效率问题仅存在于低版本浏览器ie7-及以下,现在的新浏览器基本上都解决了这个问题,效率差不多)
答案:C
2、下面代码输出结果是?
1 | var count = 0; |
- 1 1
- 2 2
- 1 2
- 1 0
这里其实也很简单,但是比较容易粗心做错,也记录下吧!
this即指向函数调用的对象,第一个调用对象是Product,Product.count就是1。第二个调用对象是Window,Window.count就是0;
另外的两个知识点就是 a++是先返回,后加。var定义的是全局变量。
答案:D
3、({} + ‘b’ > {} + ‘a’)的返回值是( )
- a
- b
- true
- false
这题我一开始想简单了,下面是我错误的推算过程:
1
2
3
4 var obj = {};
obj.valueOf().toString() === '[object Object]'
// 所以,{} + 'b' > {} + 'a' 为 true发现大错特错了。所以这题的我只知道答案,但是不知道为什么?求解!
下面是我的一些推理过程:
https://www.jb51.net/article/46753.htm
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34 {} + 'b' > {} + 'a'
false
({} + 'b' > {} + 'a')
true
({} + 'b') > ({} + 'a')
true
// 对象相加
'a' + {}
"a[object Object]"
{} + 'a'
NaN
// 数组相加(先join,后拼接)
'a' + [1,2,3]
"a1,2,3"
[1,2,3] + 'a'
"1,2,3a"
// 除了对象和数组,加法存在字符串,先转字符串,合并字符串
// 其他非加法运算先转数值,后计算。
// NaN相加/相减都是NaN, NaN永远不大于/小于/等于NaN
NaN - NaN
NaN
NaN + NaN
NaN
NaN > NaN
false
NaN < NaN
false
NaN === NaN
false
NaN == NaN
false答案:C
PS: 个人感觉这类问题无需理解,就好像无法理解
{} + [] = 0,实际工作上不会用到并且如果是TypeScript直接就会编译报错了。
4、let obj= {x: 1}; 对obj实现浅拷贝
这题谈到了对象的浅拷贝,这里扩展下,如何实现对象和数组的深浅拷贝。
大佬的细致分析见:https://segmentfault.com/a/1190000020255831
1 | // 浅拷贝方法一:Object.assign/Array.slice |
5、JavaScript改变this指向有哪些方式?
大佬更细致的分析请见:https://mp.weixin.qq.com/s/DlUJq0JJzHjnPwCI_SAI5Q
1 | fun.call(thisArg, param1, param2, ...) |
作用:改变函数执行时的this指向,目前所有关于它们的运用,都是基于这一点来进行的。
call、apply和bind是挂在Function对象上的三个方法,只有函数才有这些方法。
只要是函数就可以,比如: Object.prototype.toString就是个函数,我们经常看到这样的用法:Object.prototype.toString.call(data)
call/apply与bind的区别:
参数:
- apply是第2个参数,这个参数是一个数组:传给fun参数都写在数组中。
- call和bind从第2~n的参数都是传给fun的。
执行:
- call/apply改变了函数的this上下文后马上执行该函数
- bind则是返回改变了上下文后的函数,不执行该函数
返回值:
- call/apply 返回fun的执行结果
- bind返回fun的拷贝,并指定了fun的this指向,保存了fun的参数。
6、html5手机端适配,怎么实现1rem表示为50px?
rem是相对于根元素<html>,这样就意味着,我们只需要在根元素确定一个参考值,这个参考值设置为多少,完全可以根据您自己的需求来定。 我们知道,浏览器默认的字号16px,来看一些px单位与rem之间的转换关系:
1 | | px | rem | |
那么,要实现1rem == 50px,只需要设置html的大小为50px即可,即html { font-size:50px; }。
值得注意的是,em是相对于父级元素的字体大小,rem是相对于根元素html的字体大小,px是相对于显示器屏幕分辨率的。
em和rem也可以不仅仅给字体使用,凡是用px的地方也可以用em和rem。
7、如何准确判断一个变量是数组类型?
下面对比下js各种判断数据类型的方法和局限:
typeof1
2
3
4
5
6
7
8
9# typeof的返回值有下面几种类型:
- 'undefined' --未定义的变量或值
- 'boolean' --布尔类型的变量或值
- 'string' --字符串类型的变量或值
- 'number' --数字类型的变量或值
- 'bigint' --BigInt类型的变量或值
- 'symbol' --Symbol类型的变量或值
- 'object' --对象类型的变量或值,或者null(这个是js历史遗留问题,将null作为object类型处理)
- 'function' --函数类型的变量或值(!!!)由上面可知,当需要区分的是undefined,boolean,string,number,symbol,function, bigint这几个类型的时候的确可以使用typeof来进行判断。但是当存在Array,Map,Set等等其他类型的时候,就需要用其他方法了。
这里用个简单的记法:
fubbsns, 或者js的8种数据类型 - null - object + functioninstanceofinstanceof运算符是用来测试一个对象是否在其原型链原型构造函数的属性。其语法是object instanceof constructor可以发现:Number,String,Boolean没有检测出他们的类型,但是如果使用下面的写法则可以检测出来:
1
2
3var num = new Number(123);
var str = new String('dsfsf');
var boolean = new Boolean(false);还需要注意null,undefined都返回了false,这是因为它们的类型就是自己本身,并不是Object创建出来它们,所以返回了false。
另外,symbol不是一个构造函数,所以也是false。
另外,对于一些dom对象,window对象等都不适用。
由上面可知,限制还是很多的,所以不推荐用来判断js的内置数据类型,但是适合用来判断自定义对象类型。这点就不展开讲了,就和tom一样。
constructorconstructor运算符和instanceof运算符类似,都是基于原型链原理。constructor 属性返回对创建此对象的数组函数的引用。其语法是object.constructor1
2
3
4
5
6
7
8
9
10
11
12
13
14
15// 需要写成变量的部分
var num = 123
var obj = {a:1,b:2,c:3}
var fun = function(){console.log('aaa');}
console.log(
num.constructor==Number, //true
'aaa'.constructor==String, //true
false.constructor==Boolean, //true
[1,2,3].constructor==Array, //true
obj.constructor==Object, //true
fun.constructor==Function, //true
new Date().constructor==Date, //true
/^[a-zA-Z]{1,20}$/.constructor==RegExp, //true
new Error().constructor==Error //true
);除了undefined和null之外,其他类型都可以通过constructor属性来判断类型。
和instanceof是一样的,优势更多一些用该方法(Number,String,Boolean),其他是一样的,所以也不推荐用来判断js的内置数据类型,但是适合用来判断自定义对象类型。
Object.prototype.toString.call()对于js内置的数据类型检测最为完美的方式,但是如果你还需要判断你自定义的对象,推荐使用上面的
instanceof或者constructor。1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35function isType(data, type) {
const typeObj = {
'[object String]': 'string',
'[object Number]': 'number',
'[object Boolean]': 'boolean',
'[object Null]': 'null',
'[object Undefined]': 'undefined',
'[object Symbol]': 'symbol',
'[object Object]': 'object',
'[object Array]': 'array',
'[object Function]': 'function',
'[object Date]': 'date', // Object.prototype.toString.call(new Date())
'[object RegExp]': 'regExp',
'[object Map]': 'map',
'[object Set]': 'set',
'[object HTMLDivElement]': 'dom', // document.querySelector('#app')
'[object WeakMap]': 'weakMap',
'[object Window]': 'window', // 浏览器的全局对象,Object.prototype.toString.call(window)
'[object global]': 'global', // nodejs的全局对象,Object.prototype.toString.call(global)
'[object Error]': 'error',
'[object Arguments]': 'arguments'
}
let name = Object.prototype.toString.call(data) // 借用Object.prototype.toString()获取数据类型
let typeName = typeObj[name] || '未知类型' // 匹配数据类型
return typeName === type // 判断该数据类型是否为传入的类型
}
console.log(
isType([], 'array'), // true
isType({}, 'object'), // true
isType(new Date(), 'object'), // false
isType(new Date(), 'date'), // true
)
8、请描述一下cookie,sessionStorage和localStorage有什么区别?
cookie的内容主要包括:名字、值、过期时间、路径和域。路径与域一起构成cookie的作用范围。若不设置时间,则表示这个cookie的生命期为浏览器会话期间,关闭浏览器窗口,cookie就会消失。这种生命期为浏览器会话期的cookie被称为会话cookie。 会话cookie一般不存储在硬盘而是保存在内存里,当然这个行为并不是规范规定的。若设置了过期时间,浏览器就会把cookie保存到硬盘上,关闭后再打开浏览器这些cookie仍然有效直到超过设定的过期时间。对于保存在内存里的cookie,不同的浏览器有不同的处理方式。
共同点:都是保存在浏览器端、且同源的。
区别:
1、存储大小限制不同,cookie数据不能超过4K,同时因为每次http请求都会携带cookie、所以cookie只适合保存很小的数据,如会话
标识。sessionStorage和localStorage虽然也有存储大小的限制,但比cookie大得多,可以达到5M或更大 。
2、数据有效期不同,sessionStorage:仅在当前浏览器窗口关闭之前有效;localStorage:始终有效,窗口或浏览器关闭也一直保存,
因此用作持久数据;cookie:只在设置的cookie过期时间之前有效,即使窗口关闭或浏览器关闭 。
3、作用域不同,sessionStorage不在不同的浏览器窗口中共享,即使是同一个页面;localstorage在所有同源窗口中都是共享的;
cookie也是在所有同源窗口中都是共享的。
4、cookie数据始终在同源的http请求中携带(默认),即cookie在浏览器和服务器间来回传递,而sessionStorage和
localStorage不会自动把数据发送给服务器,仅在本地保存。
5、web Storage支持事件通知机制,可以将数据更新的通知发送给监听者。
5、web Storage的API更简单好用,Cookie的操作通常需要自己封装方法。
9、简述浏览器发起一个网络请求(HTTP请求事务)后都经历了哪些步骤?
这题感觉比较摸不着头脑,写一下我认为的答案好了。
1 | 域名解析:浏览器按照顺序解析,自身的dns缓存——客户端自身的dns缓存——本地host文件——路由器缓存 |
这是网上看到的一些相关文章:
面试环节:在浏览器输入 URL 回车之后发生了什么?(超详细版)
10、写一个原型链继承的例子
1 | // 父类 |
参考 Basarat Ali Syed 所著的《深入理解Typescript》一书第3章:JavaScript新语法特性中实现如下:(兼容ES5)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44 function __extends(/* 派生类 */d, /* 基类 */b) {
// 将基类的静态成员复制到派生类
for(var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]
// 设置派生类原型指向基类原型,即 d.prototype.__proto__ = b.prototype
function __() { this.constructor = d }
__.prototype = b.prototype
d.prototype = new __()
// // 如果支持ES6可以简写为:
// d.prototype = Object.create(b.prototype)
// d.prototype.constructor = d
}
var Point = (function() {
function Point(x, y) {
this.x = x
this.y = y
}
Point.prototype.add = function(point) {
return new Point(this.x + point.x, this.y + point.y)
}
return Point
})();
var Point3D = (function(_super) {
__extends(Point3D, _super)
function Point3D(x, y, z) {
_super.call(this, x, y)
this.z = z
}
Point3D.prototype.add = function(pointer) {
var point2D = _super.prototype.add.call(this, pointer)
return new Point3D(point2D.x, point2D.y, this.z + pointer.z)
}
return Point3D
})(Point);
var point1 = new Point3D(3, 4, 5)
var point2 = new Point3D(3, 4, 5)
console.log(point1.add(point2)) // Point3D {x: 6, y: 8, z: 10}
11、下面代码的执行结果是?
1 | for(var i = 0; i < 3; ++i){ |
这题考察的是js的异步任务队列,js会先执行主队列,然后再执行异步任务队列(异步任务队列又分为宏任务和微任务)。所以本题答案是3
这里主要拓展一下,如果需要输出0, 1, 2该怎么办呢?
答案是用ES6的let,因为let是块级别作用域
1
2
3 for(let i = 0; i < 3; ++i){
setTimeout(function(){console.log(i)}, 0)
}
本文链接: http://www.ionluo.cn/blog/posts/51a5d3af.html
版权声明: 本作品采用 知识共享署名-非商业性使用-相同方式共享 4.0 国际许可协议 进行许可。转载请注明出处!
