JS知识点总结(八)

JS知识点总结

一、怎么可以设置对象的属性不可修改?

1、使用Object.defineProperty

  • [[Configurable]]:表示属性是否可以通过 delete 删除并重新定义,是否可以修改它的特性,以及是否可以把它改为访问器属性。默认情况下,所有直接定义在对象上的属性的这个特性都是 true
  • [[Enumerable]]:表示属性是否可以通过 for-in 循环返回。默认情况下,所有直接定义在对 象上的属性的这个特性都是true
  • [[Writable]]:表示属性的值是否可以被修改。默认情况下,所有直接定义在对象上的属性的 这个特性都是true
  • [[Value]]:包含属性实际的值。这就是前面提到的那个读取和写入属性值的位置。这个特性 的默认值为 undefined
let person = {
    tjj: 1
};
Object.defineProperty(person, 'tjj', {
    configurable: false,
    writable: false
});
person.tjj = 12;
console.log(person.tjj); // 1

2、使用代理(Proxy)

const target = {
    onlyNumbersGoHere: 0
};
const proxy = new Proxy(target, {
    set(target, property, value) {
        // if (typeof value !== 'number') {
        //     return false;
        // } else {
        //     return Reflect.set(...arguments);
        // }
        return false;
    }
});

proxy.onlyNumbersGoHere = 2;
console.log(proxy.onlyNumbersGoHere); // 0

3、若使用TS

可以配置interface的属性为readonly

二、深拷贝中的问题

1、JSON.parse(JSON.stringify(obj))有什么缺点?

废话不多说,直接上代码看:

const copyObj = {
    arr: [1, 2, 3, { tjj: 'tjj' }],
    fn: () => { // ×
        console.log('123');
    },
    testBoolean: false,
    testUndefined: undefined, // ×
    testNull: null,
    testObj: {
        tjj: 1,
        arr: [1, 2, { arr: [1, { tjj: 'tjj' }] }]
    },
    testSymbol: Symbol('tjj') // ×
};

const jsonCopyObj = JSON.parse(JSON.stringify(copyObj));
console.log(jsonCopyObj);

打印结果:

{ arr: [ 1, 2, 3, { tjj: 'tjj' } ],
  testBoolean: false,
  testNull: null,
  testObj: { tjj: 1, arr: [ 1, 2, [Object] ] } }

可以看出来,这种方法可以实现递归去深拷贝源对象,但是不能拷贝函数,undefined,还有Symbol,同时由于会递归的去拷贝,还有可能会引起循环引用,导致爆栈,如:

const seven = {
    name: 'seven'
}
const juejin = {
    name: 'juejin',
    qwe: seven
}
seven.relative = juejin
const newObj = JSON.parse(JSON.stringify(seven));
console.log(newObj)

就会报循环引用错:TypeError: Converting circular structure to JSON

2、手写深拷贝(考虑循环引用)

为了避免循环引用,我们用一个全局的WeakMap保存copy过的对象。当要copy的是个对象时,判断是否copy过,若copy过直接返回result,否则添加key-value

function deepCopy3(obj) {
    const weakMap = new WeakMap();
    return helper(obj, weakMap);

    function helper(obj, weakMap) {
        let result = null;
        // 1.判断是对象类型还是普通数据类型,普通数据类型直接赋值
        if (typeof obj === 'object') {
            // 2.若是对象类型,进行细分:[],{},RegExp,null
            // 2.1若是数组
            if (Array.isArray(obj)) {
                result = [];
                for (let i = 0; i < obj.length; i++) {
                    // 递归判断一下若有对象,在进行深拷贝
                    result.push(helper(obj[i]), weakMap);
                }
            } else if (obj.constructor === RegExp) {
                result = obj;
            } else if (obj === null) {
                result = obj;
            } else { //普通对象
                result = {};
                // 若weakMap有obj,说明拷贝过,不需要再次拷贝,返回保存的值
                if (weakMap.has(obj)) {
                    result = weakMap.get(obj);
                    return result;
                } else { // 保存obj和其拷贝对象result
                    weakMap.set(obj, result);
                }
                // 保留原来对象类型,即原型上的信息
                if (obj.__proto__) {
                    result.__proto__ = obj.__proto__;
                }
                const keys = Object.keys(obj);
                for (let key of keys) {
                    result[key] = helper(obj[key], weakMap);
                }
            }
            // RegExp,null可以直接赋值,[],{}需要递归
        } else { // 普通数据类型或者函数
            result = obj;
        }
        return result;
    }

}
const seven = {
    name: 'seven',
    age: 22
}
const juejin = {
    name: 'juejin',
    qwe: seven
}
seven.relative = juejin

const copyObj2 = deepCopy3(seven);
console.log(copyObj2);
{ name: 'seven',
  age: 22,
  relative: { name: 'juejin', qwe: [Circular] } }

三、判断一个对象是否是空对象

1.JSON.stringify

function judgeObj2(emptyObj) {
    return JSON.stringify(emptyObj) === '{}';
}

2.使用for…in…

function judgeObj(emptyObj) {

    for (let key in emptyObj) {
        // 当key为undefined时不会进入循环
        return false
    }

    return true;
}

3.Object.keys

function judgeObj3(emptyObj) {
    return Object.keys(emptyObj).length === 0;
}

4.Object.getOwnPropertyNames

function judgeObj4(emptyObj) {
    return Object.getOwnPropertyNames(emptyObj).length === 0;
}

四、如何监听用户在一个页面上停留了多长时间?

1.多页面应用

可以监听onpageshow,在页面展示的时候记录时间戳,再监听onpagehide,在页面隐藏的时候计算时间长度。


let stopTime

window.onpageshow = ()=>{
  stopTime = new Date().getTime()
}

window.onpagehide = ()=>{
  stopTime = new Date().getTime() - stopTime
  let record = localStorage.getItem('data')
  let data = record && JSON.parse(record) || []
  localStorage.setItem('data',JSON.stringify([...data,{user:new Date().getTime(),path:window.location.href,stopTime}]))
}

2.单页面应用

在单页面应用中,是通过history.pushState来更新路由,渲染组件的。因此,我们需要监听pushState事件。但pushState并没有原生的监听事件,我们需要自定义一个事件,当调用pushStatedispatch这个事件。对pushStaterepalceState进行改写。

const button = document.querySelector('.button');
    button.addEventListener('click', function() {
            history.pushState({
                forward: 1
            }, 'title', `${location.href}/path`);
        })
        // 对原函数做一个拓展
    let rewriteHis = function(type) {
        let origin = window.history[type] // 先将原函数存放起来
        return function() { // 当window.history[type]函数被执行时,这个函数就会被执行
            let rs = origin.apply(this, arguments) // 执行原函数
            let e = new Event(type.toLocaleLowerCase()) // 定义一个自定义事件
            e.arguments = arguments // 把默认参数,绑定到自定义事件上,new Event返回的结果,自身上是没有arguments的
            window.dispatchEvent(e) // 触发自定义事件,把载荷传给自定义事件
            return rs
        }
    }

    window.history.pushState = rewriteHis('pushState') // 覆盖原来的pushState方法

    window.history.replaceState = rewriteHis('replaceState') // 覆盖原来的replaceState方法

    // 监听自定义事件, pushstate事件是在rewriteHis时注册的,不是原生事件
    // 当点击router-link 或者 window.history.pushState 或者 this.$router.push 时都会被该事件监听到
    window.addEventListener('pushstate', () => {
        console.log('pushstate');
    })

    // 监听自定义事件, replacestate事件是在rewriteHis时注册的,不是原生事件
    // 当点击window.history.replaceState 或者 this.$router.replace 时都会被该事件监听到
    window.addEventListener('replacestate', () => {})

3.利用webSocket

利用webSocket监听connetdisconnect时间来判断用户停留时间。
参考博文:
[1]: https://juejin.cn/post/6905913200060366862#heading-3

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值