Explorar o código

use more efficient Object check

Evan You %!s(int64=12) %!d(string=hai) anos
pai
achega
c67685b713
Modificáronse 8 ficheiros con 86 adicións e 57 borrados
  1. 9 1
      src/compiler.js
  2. 1 2
      src/directive.js
  3. 7 3
      src/directives/repeat.js
  4. 1 1
      src/filters.js
  5. 10 5
      src/main.js
  6. 18 26
      src/observer.js
  7. 14 6
      src/utils.js
  8. 26 13
      test/unit/specs/utils.js

+ 9 - 1
src/compiler.js

@@ -107,7 +107,15 @@ function Compiler (vm, options) {
     compiler.setupObserver()
 
     // initialize data
-    var data = compiler.data = options.data || {}
+    var data = compiler.data = options.data || {},
+        defaultData = options.defaultData
+    if (defaultData) {
+        for (key in defaultData) {
+            if (!hasOwn.call(data, key)) {
+                data[key] = defaultData[key]
+            }
+        }
+    }
 
     // copy paramAttributes
     var params = options.paramAttributes

+ 1 - 2
src/directive.js

@@ -135,8 +135,7 @@ function parseFilter (filter, compiler) {
  *  during initialization.
  */
 DirProto.update = function (value, init) {
-    var type = utils.typeOf(value)
-    if (init || value !== this.value || type === 'Object' || type === 'Array') {
+    if (init || value !== this.value || (value && typeof value === 'object')) {
         this.value = value
         if (this._update) {
             this._update(

+ 7 - 3
src/directives/repeat.js

@@ -33,8 +33,12 @@ module.exports = {
 
     update: function (collection) {
 
-        if (utils.typeOf(collection) === 'Object') {
-            collection = utils.objectToArray(collection)
+        if (!Array.isArray(collection)) {
+            if (utils.isObject(collection)) {
+                collection = utils.objectToArray(collection)
+            } else {
+                utils.warn('v-repeat only accepts Array or Object values.')
+            }
         }
 
         // if initiating with an empty collection, we need to
@@ -50,7 +54,7 @@ module.exports = {
         this.oldCollection = this.collection
         collection = this.collection = collection || []
 
-        var isObject = collection[0] && utils.typeOf(collection[0]) === 'Object'
+        var isObject = collection[0] && utils.isObject(collection[0])
         this.vms = this.oldCollection
             ? this.diff(collection, isObject)
             : this.init(collection, isObject)

+ 1 - 1
src/filters.js

@@ -168,7 +168,7 @@ filters.orderBy.computed = true
  */
 function contains (val, search) {
     /* jshint eqeqeq: false */
-    if (utils.typeOf(val) === 'Object') {
+    if (utils.isObject(val)) {
         for (var key in val) {
             if (contains(val[key], search)) {
                 return true

+ 10 - 5
src/main.js

@@ -98,6 +98,13 @@ function extend (options) {
 
     var ParentVM = this
 
+    // extend data options need to be copied
+    // on instantiation
+    if (options.data) {
+        options.defaultData = options.data
+        delete options.data
+    }
+
     // inherit options
     options = inheritOptions(options, ParentVM.options, true)
     utils.processOptions(options)
@@ -149,10 +156,8 @@ function inheritOptions (child, parent, topLevel) {
     for (var key in parent) {
         if (key === 'el') continue
         var val = child[key],
-            parentVal = parent[key],
-            type = utils.typeOf(val),
-            parentType = utils.typeOf(parentVal)
-        if (topLevel && type === 'Function' && parentVal) {
+            parentVal = parent[key]
+        if (topLevel && typeof val === 'function' && parentVal) {
             // merge hook functions into an array
             child[key] = [val]
             if (Array.isArray(parentVal)) {
@@ -162,7 +167,7 @@ function inheritOptions (child, parent, topLevel) {
             }
         } else if (
             topLevel &&
-            (type === 'Object' || parentType === 'Object')
+            (utils.isTrueObject(val) || utils.isTrueObject(parentVal))
             && !(parentVal instanceof ViewModel)
         ) {
             // merge toplevel object options

+ 18 - 26
src/observer.js

@@ -3,21 +3,17 @@
 var Emitter  = require('./emitter'),
     utils    = require('./utils'),
     // cache methods
-    typeOf   = utils.typeOf,
     def      = utils.defProtected,
+    isObject = utils.isObject,
+    isArray  = Array.isArray,
     hasOwn   = ({}).hasOwnProperty,
     oDef     = Object.defineProperty,
     slice    = [].slice,
-    // types
-    OBJECT   = 'Object',
-    ARRAY    = 'Array',
     // fix for IE + __proto__ problem
     // define methods as inenumerable if __proto__ is present,
     // otherwise enumerable so we can loop through and manually
     // attach to array instances
-    hasProto = ({}).__proto__,
-    // lazy load
-    ViewModel
+    hasProto = ({}).__proto__
 
 // Array Mutation Handlers & Augmentations ------------------------------------
 
@@ -157,9 +153,7 @@ def(ObjProxy, '$delete', function (key) {
  *  Check if a value is watchable
  */
 function isWatchable (obj) {
-    ViewModel = ViewModel || require('./viewmodel')
-    var type = typeOf(obj)
-    return (type === OBJECT || type === ARRAY) && !(obj instanceof ViewModel)
+    return typeof obj === 'object' && obj && !obj.$compiler
 }
 
 /**
@@ -196,11 +190,10 @@ function propagateChange (obj) {
  *  Watch target based on its type
  */
 function watch (obj) {
-    var type = typeOf(obj)
-    if (type === OBJECT) {
-        watchObject(obj)
-    } else if (type === ARRAY) {
+    if (isArray(obj)) {
         watchArray(obj)
+    } else {
+        watchObject(obj)
     }
 }
 
@@ -279,7 +272,7 @@ function convertKey (obj, key) {
     function init (val, propagate) {
         values[key] = val
         emitter.emit('set', key, val, propagate)
-        if (Array.isArray(val)) {
+        if (isArray(val)) {
             emitter.emit('set', key + '.length', val.length, propagate)
         }
         observe(val, key, emitter)
@@ -293,11 +286,11 @@ function convertKey (obj, key) {
  *  all of its properties.
  */
 function emitSet (obj) {
-    var type = typeOf(obj),
-        emitter = obj && obj.__emitter__
-    if (type === ARRAY) {
+    var emitter = obj && obj.__emitter__
+    if (!emitter) return
+    if (isArray(obj)) {
         emitter.emit('set', 'length', obj.length)
-    } else if (type === OBJECT) {
+    } else {
         var key, val
         for (key in obj) {
             val = obj[key]
@@ -314,19 +307,18 @@ function emitSet (obj) {
  *  emit a set event with undefined value.
  */
 function copyPaths (newObj, oldObj) {
-    if (typeOf(oldObj) !== OBJECT || typeOf(newObj) !== OBJECT) {
+    if (!isObject(newObj) || !isObject(oldObj)) {
         return
     }
-    var path, type, oldVal, newVal
+    var path, oldVal, newVal
     for (path in oldObj) {
         if (!(hasOwn.call(newObj, path))) {
             oldVal = oldObj[path]
-            type = typeOf(oldVal)
-            if (type === OBJECT) {
+            if (isArray(oldVal)) {
+                newObj[path] = []
+            } else if (isObject(oldVal)) {
                 newVal = newObj[path] = {}
                 copyPaths(newVal, oldVal)
-            } else if (type === ARRAY) {
-                newObj[path] = []
             } else {
                 newObj[path] = undefined
             }
@@ -348,7 +340,7 @@ function ensurePath (obj, key) {
         }
         obj = obj[sec]
     }
-    if (typeOf(obj) === OBJECT) {
+    if (isObject(obj)) {
         sec = path[i]
         if (!(hasOwn.call(obj, sec))) {
             obj[sec] = undefined

+ 14 - 6
src/utils.js

@@ -5,6 +5,7 @@ var config    = require('./config'),
     timeout   = win.setTimeout,
     def       = Object.defineProperty,
     THIS_RE   = /[^\w]this[^\w]/,
+    OBJECT    = 'object',
     hasClassList = 'classList' in document.documentElement,
     ViewModel // late def
 
@@ -90,11 +91,18 @@ var utils = module.exports = {
     },
 
     /**
-     *  Accurate type check
-     *  internal use only, so no need to check for NaN
+     *  A less bullet-proof but more efficient type check
+     *  than Object.prototype.toString
      */
-    typeOf: function (obj) {
-        return toString.call(obj).slice(8, -1)
+    isObject: function (obj) {
+        return typeof obj === OBJECT && obj && !Array.isArray(obj)
+    },
+
+    /**
+     *  A more accurate but less efficient type check
+     */
+    isTrueObject: function (obj) {
+        return toString.call(obj) === '[object Object]'
     },
 
     /**
@@ -192,7 +200,7 @@ var utils = module.exports = {
      */
     toConstructor: function (obj) {
         ViewModel = ViewModel || require('./viewmodel')
-        return utils.typeOf(obj) === 'Object'
+        return utils.isObject(obj)
             ? ViewModel.extend(obj)
             : typeof obj === 'function'
                 ? obj
@@ -284,7 +292,7 @@ var utils = module.exports = {
         var res = [], val, data
         for (var key in obj) {
             val = obj[key]
-            data = utils.typeOf(val) === 'Object'
+            data = utils.isObject(val)
                 ? val
                 : { $value: val }
             data.$key = key

+ 26 - 13
test/unit/specs/utils.js

@@ -112,18 +112,31 @@ describe('Utils', function () {
 
     })
 
-    describe('typeOf', function () {
+    describe('isObject', function () {
         
-        it('should return correct type', function () {
-            var tof = utils.typeOf
-            assert.equal(tof({}), 'Object')
-            assert.equal(tof([]), 'Array')
-            assert.equal(tof(1), 'Number')
-            assert.equal(tof(''), 'String')
-            assert.equal(tof(true), 'Boolean')
-            // phantomjs weirdness
-            assert.ok(tof(null) === 'Null' || tof(null) === 'DOMWindow')
-            assert.ok(tof(undefined) === 'Undefined' || tof(undefined) === 'DOMWindow')
+        it('should return correct result', function () {
+            var iso = utils.isObject
+            assert.ok(iso({}))
+            assert.notOk(iso([]))
+            assert.notOk(iso(1))
+            assert.notOk(iso(true))
+            assert.notOk(iso(null))
+            assert.notOk(iso(undefined))
+        })
+
+    })
+
+    describe('isTrueObject', function () {
+        
+        it('should return correct result', function () {
+            var iso = utils.isTrueObject
+            assert.ok(iso({}))
+            assert.notOk(iso([]))
+            assert.notOk(iso(1))
+            assert.notOk(iso(true))
+            assert.notOk(iso(null))
+            assert.notOk(iso(undefined))
+            assert.notOk(iso(document.createElement('div')))
         })
 
     })
@@ -264,9 +277,9 @@ describe('Utils', function () {
         it('should convert plain object components & elements to constructors', function () {
             var components = options.components
             assert.ok(components.a.prototype instanceof Vue)
-            assert.strictEqual(components.a.options.data.data, 1)
+            assert.strictEqual(components.a.options.defaultData.data, 1)
             assert.ok(components.b.prototype instanceof Vue)
-            assert.strictEqual(components.b.options.data.data, 2)
+            assert.strictEqual(components.b.options.defaultData.data, 2)
         })
 
     })