javascript
为什么typeof null是object?
最初的js是运行在32位机器上,为了性能方面考虑选择了低位存储变量的信息,000开头就表示是对象,而null表示全是零
如果判断对象内某个key是自身创建而非继承获得
const c = {a:123}
c.hasOwnProperty('a')
c.hasOwnProperty('toString')
obj.child = obj = {num: 2} 输出什么
var obj = {num: 1}
obj.child = obj = { num: 2 }
console.log(obj.child)
['1', '2', '3'].map(parseInt)输出什么
[1, NaN, NaN],
[].map(function(val, index, arr){})
上述代码会经历以下阶段
- parseInt('1', 0, arr)
- parseInt('2', 1, arr)
- parseInt('3', 2, arr)
parseInt接收两个参数,一个字符串,一个radix,
- radix为0/null/undefined时候,默认输出1,
- radix为1时候,因为radix只接收2-36的数值,导致无效,返回NaN
- radix为2时候,只会生成二进制0/2,所以仍会返回NaN
关于radix,
如果parseInt第一个参数是0开头的,radix会被默认设置成8进制或者10进制,具体根据浏览器决定,在ESMAScript5中 被确定,应该使用10进制
跨窗口交互方案有哪些
以下代码中a,b,c会输出什么
function A() {
this.a = 'a';
}
A.prototype.say = () => {
return 'b'
}
const a = new A();
function B() {
return 'b';
}
B.prototype.say = () => {
return 'b'
}
const b = new B();
function C() {
return {
name: 'c'
};
}
C.prototype.say = () => {
return 'c'
}
const c = new C();
实现结构对象const [a, b] = {a:1,b:2}
根据可迭代协议,为对象设置迭代函数
Object.prototype[Symbol.iterator] = function*() {
return yield *Object.values(this)
}
Object.prototype[Symbol.iterator] = function() {
return Object.values(this)[Symbol.iterator]()
}
Object.prototype[Symbol.iterator] = function*() {
for(const key in this) {
yield this[key]
}
}
Object.prototype[Symbol.iterator] = function() {
const values = Object.values(this);
return {
next() {
const val = values.shift();
return {
value: val,
done: !val
}
}
}
}
const obj = {
a: 1,
b: 2,
}
const [a, b] = obj;
console.log(a /* a = 1 */,b, /* b = 2*/);
for(const val of obj) {
console.log('----', val);
}
typeof和instanceof的区别
typeof和instanceof都可以用来判断数据的类型
- typeof用来判断数据的基本数据类型
- instanceof用来判断数据的引用类型
实现instanceof
class C1 extends Array {}
const _c1 = new C1();
function instance_of(current, Status) {
let res = current.__proto__;
const StatusProt = Status.prototype;
while (res) {
if (res === StatusProt) return true;
res = res.__proto__;
}
return false
}
console.log(instance_of(_c1, Array));
类数组转换
const list = document.getElementsByTagName('div');
// 利用原型链
Array.prototype.slice.call(list)
// es6方法
Array.from(list)
// 解构
[...list]
为什么有时候发送post请求时候会预先发送一个option请求,这个请求是干什么的
简单描述:预请求 面试描述:下文解析 解析[juejin.cn/post/726995…]
最简方式实现代码深拷贝,Object.assign是深拷贝还是浅拷贝?
const data = {
a: 1,
b: 2
}
const cloneEs6 = (value) => {
return new Promise(resolve => {
const message = new MessageChannel();
message.port1.onmessage = e => {
resolve(e.data);
}
message.port2.postMessage(value)
})
}
const d2 = await cloneEs6(data);
手动实现call/apply/bind
const apply = (fn, $this, args) => {
const a = Object.assign({}, $this, { fn });
return a.fn(...args);
}
const call = (fn, $this, ...args) => {
const a = Object.assign({}, $this, { fn });
return a.fn(...args);
}
const bind = (fn, $this) => {
const a = Object.assign({}, $this, { fn });
return (...args) => a.fn(...args);
}
const getSchool = function(a, b) {
return `${this.name}_${a},_${b}`;
}
const a1 = apply(getSchool, { name: 'tomg'}, ['a1', 'b1']);
const a2 = call(getSchool, { name: 'tomg'}, 'a2', 'b2');
const a3 = bind(getSchool, { name: 'tomg'});
script标签defer和async的区别
- 设置defer,脚本异步加载,加载完之后,在$(body).ready/DOMContentLoaded事件触发之前执行
- 设置async,脚本异步加载,加载完立即解析,会阻碍dom渲染
Promise输出结果
console.log('1');
function promiseFn() {
return new Promise((resolve, reject) => {
setTimeout(()=> {
console.log('2');
})
resolve('3');
console.log('4')
})
}
promiseFn().then(res => {
console.log(res);
});
答案:1 4 3 2
一维数组转三维数组
[1,2,3,4,5,6,7,8,9] => [[1,2,3], [4,5,6], [7,8,9]]
答案
const convert = (list, length, newList = []) => {
let idx = 0;
while (idx < list.length) {
newList.push(list.slice(idx,idx + length))
idx += length;
}
return newList;
}
console.log(convert([1,2,3,4,5,6,7,8,9], 3))
实现a == 1 && a == 2 && a == 3
// 实现1
const a = {
_a: 1,
valueOf() {
return this._a++;
},
}
// 实现2
const a = {
_a: 1,
toString() {
return this._a++;
},
}
console.log(a == 1 && a == 2 && a == 3);
// 实现3
const a = {
_a: 1,
[Symbol.toPrimitive]() {
return this._a++;
},
}
console.log(a == 1 && a == 2 && a == 3);
假设一个函数返回一个对象,在不直接修改返回对象的情况下,怎么修改返回对象里的值
function getInfo() {
return {
name: '张三',
age: 14,
}
}
const info = getInfo();
/**
* 禁止使用info.name = '李四'
*/
// 代码------start
// 代码------end
console.log(info.name === '李四')
答案
Object.prototype.__update = function() {
this.name = '李四'
}
info.__update();
delete Object.prototype.__update;
针对上述问题,如何实现一个安全的沙盒系统(web平台)
要求,这个禁止修改主环境内全局对象,防止出现上述问题
document.abc = '123123'
const code = `
const name = 'tom';
Object.prototype.name = 12323;
console.log(123, document.abc, Object.prototype.name)
`
run(code);
console.log(Object.prototype.name)
答案
class createSandbox {
constructor() {
this.init();
this.proxy();
}
$iframe;
$context;
init() {
this.$iframe = document.createElement('iframe');
document.body.appendChild(this.$iframe)
}
code(code) {
const Fn = new Function(`window`, `with(window) {
${code}
}`)
Fn(this.$context)
}
blacklist = ['Math'];
proxy() {
const that = this;
this.$context = new Proxy(this.$iframe.contentWindow, {
set(target, key, value) {
return Reflect.set(targeet, key, value)
},
get(target, key) {
if (that.blacklist.includes(key)) {
return undefined
}
return Reflect.get(target, key);
}
})
}
}
document.abc = '123123'
const sanbox = new createSandbox();
sanbox.code(`
const name = 'tom';
Object.prototype.name = 12323;
console.log(123, document.abc, Math, Object.prototype.name)
`)
Promise都有哪些方法,并且都是什么作用
- Promise.all
-
- 只要有一个reject就会走catch
- Promise.allSettled
-
- 无论resolve还是reject,都返回给then
- Promise.race
-
- 如果有一个resolve或者reject了,就传给then/catch
- Promise.any
-
- 只要有一个resolve了,就传给then
- Promise.resolve
-
- 创建一个resolve promise
- Promise.reject
-
- 创建一个reject promise
React
useCallback的作用
可以用来缓存函数
如果子组件接收一个函数,并且子组件是用memo包裹的情况下,可以优化减小组件的渲染次数
子组件里可以用useEffect来监听 useCallback包裹的函数发生变化
react captrue value的特性原理
下面的代码有问题有问题吗,点击三次按钮,页面num显示是多少
import { useEffect, useRef } from 'react';
import { flushSync } from 'react-dom';
const Zhuce = () => {
const $el = useRef(null as any as HTMLDivElement);
const [num, setNum] = useState(0);
useEffect(() => {
var div = document.createElement('button');
div.onclick = () => {
setNum(num+1);
}
div.textContent = 'aaaa';
$el.current.appendChild(div)
}, [])
return <div ref={$el}>
{num}
</div>
}
export default Zhuce;
上述问题是什么原因导致?
...
如何解决上述问题?
const add = useRef(() => setNum(num+1));
add.current = useMemo(() => () => setNum(num+1), [num])
useEffect(() => {
var div = document.createElement('button');
div.onclick = () => {
// setNum(num+1);
add.current();
}
div.textContent = 'aaaa';
$el.current.appendChild(div)
}, [])
优化
如何提高h5首屏打开速度
- 网络层次
-
- http2协议访问
- headers文件缓存
- 域名分片
- cdn
- 减少不必要301/302
- 代码层次
-
- 懒加载(路由页面懒加载/页面内部模块懒加载)
- 非必要自定义字体
- script资源async/defer
- localStorage缓存必要数据,等接口返回刷新数据
- 与主功能无关元素延迟加载,例如广告/埋点等
- 大量数据渲染,采用分段渲染防止页面卡住
- 避免img src空,会浪费额外请求
- 骨架屏
- 预取回(prefetch)/预加载(preload)/预连接(preconnect)/dns预解析(dns-prefetch)
- 静态文件
-
- 图片雪碧图/base64
- 优先webp格式图片
其他
什么是cdn
构建在现有基础网络之上的虚拟智能网络,依托于各地的边缘服务器,通过中心服务器的调度负载均衡等功能,让用户可以就近访问所需内容,降低网络阻塞,提供访问速度。
cdn首次请求返还的是一个cname而非id,通过cname转到实际请求的ip地址
当用到一个需要实时数据更新的图表页面时候如何进行数据获取
- 长链接 (兼容性最好,但是需要不断的请求)
- websocket (创建一个长链接与服务器实时沟通,使用了新的协议)
- sse (不考虑客户端向服务端推送情况下,当向数据接收,基于的http协议,兼容性好)
关于http缓存
http请求头中,cache-control都有哪些常用值?
- max-age 强缓存,设置最大有效期,单位秒
- public 客户端以及代理服务器都可以被缓存
- private 客户端可以被缓存
- no-cache 走协商缓存
- no-store 禁止任何缓存
如何设置协商缓存?
- last-modified,服务端返回的资源最后修改时间,当下次请求时候,http请求头里会带上这个字段传给服务端(if-Modified-Since),服务端会根据此字段进行对比是否需要进行资源更新
- etag,服务端生成的资源唯一表示,服务端会根据此字段进行比较,如果不同则更新资源
为什么有了last-modified,还会有etag?
一个文件如果在里面添加了一些内容,之后有删除了这些内容,这个文件本质没有发生任何变化,但是它的修改时间变了,如果紧依赖于last-modified,就会触发文件更新流程,
max-age(强缓存) > expires(强缓存) > etag(协商缓存) > last-modified(协商缓存)