Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

underscore源码系列之 for...in #33

Open
jyzwf opened this issue Dec 3, 2017 · 0 comments
Open

underscore源码系列之 for...in #33

jyzwf opened this issue Dec 3, 2017 · 0 comments

Comments

@jyzwf
Copy link
Owner

jyzwf commented Dec 3, 2017

var hasEnumBug = !{ toString: null }.propertyIsEnumerable('toString')   
    var nonEnumerableProps = ['valueOf', 'isPrototypeOf', 'toString',
        'propertyIsEnumerable', 'hasOwnProperty', 'toLocaleString'];    // 不能用for... in 遍历的 key 集合


    var collectNonEnumProps = function (obj, keys) {
        var nonEnumIdx = nonEnumerableProps.length;
        var constructor = obj.constructor;
        // 对象的原型如果被重写,则 proto 为 Object.prototype
        // 反之,则为 obj.constructor.prototype
        
        var proto = _.isFunction(constructor) && constructor.prototype || ObjProto


        // 如果obj 有 constructor 这个key 
        // 并且没有存在数组中,就存入数组
        var prop = 'constructor'
        if (_.has(obj, prop) && !_.contains(keys, prop)) keys.push(prop)


        while (nonEnumIdx--) {
            prop = nonEnumerableProps[nonEnumIdx]
            // prop 在对象里面,并且与Object原型上的不是同一个引用,且不包含在keys 中
            if (prop in obj && obj[prop] !== proto[prop] && !_.contains(keys, prop)) {
                keys.push(prop)
            }
        }
    }


    // 取出对象的所有属性名
    _.allKeys = function (obj) {
        if (!_.isObject(obj)) return []  // 不是对象,直接返回空数组
        var keys = []
        for (var key in obj) keys.push(key)

        // 处理 ie < 9 以下的情况
        if (hasEnumBug) collectNonEnumProps(obj, keys)
        return keys
    }

上面这段代码中,除了一般的使用 for...in 来取出属性名,后面还有一步判断是否有 可枚举的bug 情况,主要针对,ie<9下,for...in 的问题。不过我在 mdn 上查到 for...in在 ie6 下都支持,所以可能之前编写这个库时 for...in 在ie<9下都不支持

image

不过阅读这段代码还是能收获不少

首先

var hasEnumBug = !{ toString: null }.propertyIsEnumerable('toString')

在 ie<9 下,{ toString: null }.propertyIsEnumerable('toString') 会返回 false,因此可以用此来判断是否是 ie<9 的浏览器

注:现在ie 下的情况:
image

var nonEnumerableProps = ['valueOf', 'isPrototypeOf', 'toString', 'propertyIsEnumerable', 'hasOwnProperty', 'toLocaleString'];
这是一些不可枚举的属性,除了 constructor
image

var proto = _.isFunction(constructor) && constructor.prototype || ObjProto
这里上面代码里面注释了一些,不过还要说的一点是, _.isFunction(constructor) && constructor.prototype 排除了 对象的 constrcutor 是直接量的问题,并且排除了 var a = {}; a.__proto__ = null 的情况,如果 constrcutor 是上述两种情况 (a.__proto__ = nulla.constructor = undefined),则返回 ObjProto

后来又想了下,如果 constructor 是函数,那么它 的原型一定存在:
image

那么后面何必再判断 constructor.prototype 呢?有一点可以肯定的是如果 自己手动将 constructor.prototype = null 的时候这个判断是有效的,其他的确实想不到为什么要这个来判断,如果有谁懂,望告知

接着是 将 constructor 这个属性单独拿出来判断(但是为什么要这么做呢??不能也放入上述数组中??求告知)
其中 _.has() 使用 hasOwnProperty来判断,保证了不包含 原型上的 constructor

最后就是循环判断那6个不可枚举属性
其中 prop in obj很重要,那为啥要加这句呢?

var a = {},
      keys = []

a.__proto__ = null
collectNonEnumProps(a, keys);
// 如果没有 `prop in obj`,那么返回的是 ` keys=['valueOf', 'isPrototypeOf', 'toString', 'propertyIsEnumerable', 'hasOwnProperty', 'toLocaleString']`
// 如果有,keys = []

最后,大家尽量少改写原生对象的属性,避免不必要的错误

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant