十道小题(一)

157 阅读9分钟

「这是我参与2022首次更文挑战的第21天,活动详情查看:2022首次更文挑战

1 页面统计数据中,常用的PV、UV指标值得是什么?

PV (页面访问量)

page visitor 页面的浏览量或点击量,点击进去就算一次,退出再进去又一次。不限制是否同一用户。

UV (独立访客)

unique visitor 是指通过互联网访问这个网页的自然人。这个在没有登录的情况下,是以终端计的,多个人使用同一个设备轮流访问一个网址, 在00:00-24:00内也只会算一次。

2判断数据类型的方式有哪些?

  • typeof
  • instanceof
  • constructor
typeof

typeof用以获取一个变量或者表达式的类型-一般用于判断值类型

问题:arr, object, date, reg, error 全部被识别为object。而number, string, boolean, function, undefined, symbol, bigint可以被识别 typeof null === 'object'. null和 undefined 直接用===判断即可 另外,注意NaN就是number ,期望是一个number但无法用number来表示, typeof NaN === 'number'

适用于判断,原始值类型.复杂数据类型无法准确判断,毕竟js万物皆对象,其余类型都返回object。

image.png

instanceof

instanceof 运算符是用来判断一个对象是否在其原型链原型构造函数的属性,所以在比较对象(引用类型)时才有意义。

问题:instanceof不能判断值类型,但是引用类型可以,另外arrobjinstanceof Object的时候的值都是true

也因此只要在它的原型链上,就返回true ,这是一个二元操作符。 而Object位于原型链的顶端。已知要比较的类型,才能判断

constructor

constructor本来是原型对象上的属性,指向构造函数。但是根据实例对象寻找属性的顺序,若实例对象上没有实例属性或方法时,就去原型链上寻找,因此,实例对象也是能使用constructor属性的

问题:undefined和null没有constructor属性,所以判断时代码可能会报错。由于constructor属性是可以变更的,也会导致检测出的结果不正确

其他方法

复杂对象可用toString 方法来查看,具体的可以打印看看。 数组专用的isArray `Object.prototype.toString Array.isArray() isNaN()

`

3 new 一个函数发生了什么

红宝书上写的很清楚了。 不过,我就喜欢找问题的问题。如果这个函数不是构造函数,还能new 吗。答案是可以,看来这题没啥毛病。

1 在内存中创建一个新的对象
2 这个新对象的特性[[prototype]]被赋值为的构造函数的prototype属性。    也就是说`obj.__proto__ = constructor.prototype `.
3 (构造)函数内部的this被指向这个新的对象。
4  执行代码
5  如果函数最终没有返回非空对象,则返回这个新对象。   也就是说要么这个函数没有返回值,要么返回值不是一个对象。 这里的非空指的是逻辑空 。因为null 也是object

最后 如何判断一个函数是不是构造函数?

无法准确判断。

如果定义这个函数时,首字母大写,那么会按构造函数解析。不使用new 关键字,直接使用构造函数,也就是没有指明this,老规矩,this指向当前上下文或者globalThis. 结果就是给相应的上下文增添了构造函数类的属性和方法。

如果使用new 关键字,那么按上述过程进行。

回答的时候,注意两点,一是函数内部this的指向,二是返回值

4浏览器导航 跳转 刷新 前进后退。

a标签

a 标签的herf属性就是地址栏中的url

target可以选择_blank新标签页, _self当前窗口,_parent负窗口,_top 顶层父窗口. 默认值_self, 。

那么问题来了,a标签能实现刷新效果吗?

不写事件的情况下不行。

location

一般是直接使用window.location.虽然document.location也是同一个类型。

  • 跳转 给location.herf直接赋值就等同于跳转,和a标签不同的是,如果herf新的赋值就是当前url会刷新。 location 有一个专门的方法 assign (url) 用于跳转url.似乎没谁用这个,大部分人都直接修改herf属性
    location 还有一个额外的方法 replace(url),和assign方法不同的是,这种跳转方式如同清空了历史记录,然后跳转到新的页面,也就是说,跳转之后,是新的开始,无法前进后退。
  • 刷新 location 有专用的刷新方法 reload(bool) 这个方法接收一个参数,表示是否强制从服务器重新加载资源,也就是当参数为true时,一定不会从缓存中加载资源,也就是我们常说的强制刷新

location 要想前进后退就必须知道前进后退的url,也就是不能前进后退

window.location.assign("http://www.mozilla.org"); // or
window.location = "http://www.mozilla.org";
window.location.replace("http://www.mozilla.org") 
window.location.reload(true);

history

使用过vuerouter的人可能对 history 更熟悉。

  • 跳转 history.go(n) go方法指定去到相对当前记录的某个记录。0就是刷新,1前进一个记录,-1后退一个记录。 history没有直接跳转的方法。history设计出来是用来管理浏览历史状态的。
  • 刷新 history.go(0)
  • 前进 history.forword() 效果等同history.go(1)
  • 后退 history.back() 效果等同history.go(-1)

history的另外两个方法 pushState和replaceState 是用来修改历史状态栈的。pushState新增一个历史会话记录,replaceState直接修改历史会话。效果和location的 assign 和replace比较像。

不同的是这两个方法虽然都会修改地址栏,但是不会触发跳转。满足了某些产品的奇葩需求。并且最后一个参数url必须是和当前地址同源。所以这两个方法和本题无关。

open

open方法,常用于打开新窗口。

let windowObjectReference = window.open(strUrl, strWindowName, [strWindowFeatures]);

返回值是窗口的引用,我们这里只关注,第二个参数,也就是新的窗口名,不提供的话默认是本窗口新标签页,不会打开新窗口。 如果提供和a标签的target的四个值,也是老窗口新标签。

image.png

小结

跳转使用location的 assign 或者replace
刷新使用location的reload 或者history.go(0)
前进使用history的 forward 或者history.go(1)
前进使用history的 back 或者history.go(-1) \

5 a.x = a = {}

一道关于连续赋值和对象引用的经典八股。原题是直接打印a.c,结果自然是undefined. 这里我把原本对象引用保留了一份,能更清楚的看明白。

 {
        let a = { b: 9}, a2 = a ;
        a.c  = a = {x:10 }
        console.log( a, a2)
    }

也就是要注意在连续赋值的一行代码中,如果有引用类型的标识符,那么同一个标识符,一个的修改不会影响另一个。

我们都知道,赋值是从右往左,计算从往右。 可以理解为在一行赋值语句中,等号左边每个标识符都是一个临时副本,互不干扰。 我们利用解构赋值来交换两个标识符的值,也是这个应用。 还有什么 let n = 0, b = ++n + n++之类的八股都是同样的道理

image.png

image.png

这里主要就是注意返回值和赋值顺序,一个赋值语句的返回值其实就是左边的标识符的返回值。

6 symbol的用途

首先Symbol的使用,不支持用new关键字,es规定它是原始值。 Symbol接收一个参数,作为这个符号的描述,返回值与入参无关。

每次调用Symbol 都会返回一个独一无二的symbol, 所以一般用途就是作为唯一标识。 可以用作对象的key(这似乎就打破了对象只能用字符串作为键),当然也能用作值。 注意Object.keys()无法访问symbol键,for in 自然也不行,只有 Object.getOwnPropertySymbols()专门用于获取对象的symbol属性, 它返回一个包含全部符号键的数组。

一旦使用symbol作为对象的键,由于其独一无二性,如果不事先保留了symbol的值,外部就无法访问到这属性,也就不能修改。这就在一定程度上模拟了私有属性。

image.png

Symbol上还挂了一堆方法, mdn上的中文翻译居然是众所周知的,好吧我这个半路出家的人现在才知道。 我先前单知道Symbol.iterator ,用于创建对象的生成器函数作为默认迭代器。

Symbol.iterator 
Symbol.asyncIterator
Symbol.match() 被String.prototype.match使用
Symbol.replace() 被String.prototype.replace使用
Symbol.split() 被String.prototype.split使用
Symbol.search() 被String.prototype.search使用
...

还有一些不常用的,就不列举出来了,感兴趣的去mdn看吧。

回答,注意symbol的独一无二即可,独一无二当然是用来解决, 命名冲突唯一标识等问题,也可以模拟私有变量

7NaN

众所周知, NaN !== NaN 。 NaN(not a number)的出现,是因为某种计算结果,这个计算结果期望是一个Number,但不是或者无法表示,无法按照正常的数学运算进行。 比如 对象转数字,1+ undefind, 非number值和 Number值进行了加法以外的运算。 NaN仍是Number类型。

任何值和NaN进行运算结果都是另一个NaN。 可以用 isNaN()来判断,这个值能否正常转为Number,如果不能则会返回true。

image.png

8判断一个对象是否空对象

方法1 通过key的有无来判断,一般就是Object.keys() 。但是这无法获取到不可遍历的属性,一般够用了。

如果想看全部属性可用 Object.getOwnPropertyDescriptors() 返回的是一个对象,有全部属性的描述。 不过Symbol类型的键你得提前知道。 Object.getOwnPropertyNames返回的keys包含 不可enumberabled的,但不包括symbol。 方法2 通过 JSON.stringify 转字符串{}, 同样无法获取不可遍历的属性。

所以,可以通过 Object.getOwnPropertyNames() 加上 Object.getOwnPropertySymbols来判断是否空对象足够了 或者使用反射,Reflect.ownKeys(target) 加上 Object.getOwnPropertySymbols

其实这个问题本身就有问题, 空对象,什么是空对象,不包含任何属性方法的对象?这是不可能的,也许一般的空对象就是指不包含除了 __proto__的任何可枚举属性。

9 判断是否数组

1 新方法 Array.isArray() 2 instanceof Array 、 constructor属性 。 注意伪数组,有length,有若干索引键的对象。 arguments是伪数组

10 伪数组 转数组

所谓伪数组, 就是它的生产厂家不是Array,所以它不能使用Array.prototype的方法。 1 新方法 Array.from() 2 扩展运算符[... ]