diff --git a/src/compiler.js b/src/compiler.js index 2cf8e274b..34d6d3588 100644 --- a/src/compiler.js +++ b/src/compiler.js @@ -44,30 +44,23 @@ function Compiler (vm, options) { // set compiler properties compiler.vm = vm + compiler.bindings = makeHash() compiler.dirs = [] compiler.exps = [] compiler.computed = [] compiler.childCompilers = [] compiler.emitter = new Emitter() - // inherit parent bindings - var parent = compiler.parentCompiler - compiler.bindings = parent - ? Object.create(parent.bindings) - : makeHash() - compiler.rootCompiler = parent - ? getRoot(parent) - : compiler - // set inenumerable VM properties def(vm, '$', makeHash()) def(vm, '$el', el) def(vm, '$compiler', compiler) - def(vm, '$root', compiler.rootCompiler.vm) + def(vm, '$root', getRoot(compiler).vm) // set parent VM // and register child id on parent - var childId = utils.attr(el, 'component-id') + var parent = compiler.parentCompiler, + childId = utils.attr(el, 'component-id') if (parent) { parent.childCompilers.push(compiler) def(vm, '$parent', parent.vm) @@ -204,7 +197,7 @@ CompilerProto.setupObserver = function () { }) function check (key) { - if (!hasOwn.call(bindings, key)) { + if (!bindings[key]) { compiler.createBinding(key) } } @@ -405,31 +398,23 @@ CompilerProto.bindDirective = function (directive) { // otherwise, we got more work to do... var binding, - compiler = this, - key = directive.key, - baseKey = key.split('.')[0] + compiler = this, + key = directive.key if (directive.isExp) { // expression bindings are always created on current compiler binding = compiler.createBinding(key, true, directive.isFn) } else { - // recursively locate where to place the binding + // recursively locate which compiler owns the binding while (compiler) { - if ( - hasOwn.call(compiler.data, baseKey) || - hasOwn.call(compiler.vm, baseKey) - ) { - // If a compiler has the base key, the directive should - // belong to it. Create the binding if it's not created already. - binding = hasOwn.call(compiler.bindings, key) - ? compiler.bindings[key] - : compiler.createBinding(key) + if (compiler.hasKey(key)) { break } else { compiler = compiler.parentCompiler } } - if (!binding) binding = this.createBinding(key) + compiler = compiler || this + binding = compiler.bindings[key] || compiler.createBinding(key) } binding.instances.push(directive) @@ -484,7 +469,7 @@ CompilerProto.createBinding = function (key, isExp, isFn) { // ensure path in data so it can be observed Observer.ensurePath(compiler.data, key) var parentKey = key.slice(0, key.lastIndexOf('.')) - if (!hasOwn.call(bindings, parentKey)) { + if (!bindings[parentKey]) { // this is a nested value binding, but the binding for its parent // has not been created yet. We better create that one too. compiler.createBinding(parentKey) @@ -585,6 +570,15 @@ CompilerProto.execHook = function (id, alt) { } } +/** + * Check if a compiler's data contains a keypath + */ +CompilerProto.hasKey = function (key) { + var baseKey = key.split('.')[0] + return hasOwn.call(this.data, baseKey) || + hasOwn.call(this.vm, baseKey) +} + /** * Unbind and remove element */ @@ -626,8 +620,8 @@ CompilerProto.destroy = function () { // unbind/unobserve all own bindings for (key in bindings) { - if (hasOwn.call(bindings, key)) { - binding = bindings[key] + binding = bindings[key] + if (binding) { if (binding.root) { Observer.unobserve(binding.value, binding.key, compiler.observer) } diff --git a/src/exp-parser.js b/src/exp-parser.js index 1cc2d69c6..b0377471c 100644 --- a/src/exp-parser.js +++ b/src/exp-parser.js @@ -1,5 +1,4 @@ var utils = require('./utils'), - hasOwn = Object.prototype.hasOwnProperty, stringSaveRE = /"(?:[^"\\]|\\.)*"|'(?:[^'\\]|\\.)*'/g, stringRestoreRE = /"(\d+)"/g @@ -54,38 +53,25 @@ function getVariables (code) { */ function getRel (path, compiler) { var rel = '', - has = false, - nest = 0, - vm = compiler.vm, - dot = path.indexOf('.'), - key = dot > -1 - ? path.slice(0, dot) - : path - while (true) { - if ( - hasOwn.call(vm.$data, key) || - hasOwn.call(vm, key) - ) { - has = true + dist = 0, + self = compiler + while (compiler) { + if (compiler.hasKey(path)) { break } else { - if (vm.$parent) { - vm = vm.$parent - nest++ - } else { - break - } + compiler = compiler.parentCompiler + dist++ } } - if (has) { - while (nest--) { + if (compiler) { + while (dist--) { rel += '$parent.' } - if (!hasOwn.call(vm.$compiler.bindings, path) && path.charAt(0) !== '$') { - vm.$compiler.createBinding(path) + if (!compiler.bindings[path] && path.charAt(0) !== '$') { + compiler.createBinding(path) } } else { - compiler.createBinding(path) + self.createBinding(path) } return rel } diff --git a/test/unit/specs/exp-parser.js b/test/unit/specs/exp-parser.js index 0601bd303..a0987ab9c 100644 --- a/test/unit/specs/exp-parser.js +++ b/test/unit/specs/exp-parser.js @@ -82,6 +82,7 @@ describe('UNIT: Expression Parser', function () { var caughtMissingPaths = [], compilerMock = { createBinding: createBinding, + hasKey: function () {}, vm:{ $data: {}, $compiler:{ @@ -120,12 +121,14 @@ describe('UNIT: Expression Parser', function () { utils.warn = function () { warned = true } + function noop () {} ExpParser.parse('a + "fsef', { - createBinding: function () {}, + createBinding: noop, + hasKey: noop, vm: { $compiler: { bindings: {}, - createBinding: function () {} + createBinding: noop }, $data: {} } diff --git a/test/unit/specs/viewmodel.js b/test/unit/specs/viewmodel.js index 1789c5b06..593e8b9df 100644 --- a/test/unit/specs/viewmodel.js +++ b/test/unit/specs/viewmodel.js @@ -371,8 +371,7 @@ describe('UNIT: ViewModel', function () { expUnbindCalled = false, bindingUnbindCalled = false, unobserveCalled = 0, - elRemoved = false, - externalBindingUnbindCalled = false + elRemoved = false var dirMock = { binding: { @@ -385,26 +384,21 @@ describe('UNIT: ViewModel', function () { } dirMock.binding.instances.push(dirMock) - var bindingsMock = Object.create({ - 'test2': { - unbind: function () { - externalBindingUnbindCalled = true - } - } - }) - bindingsMock.test = { - root: true, - key: 'test', - value: { - __observer__: { - off: function () { - unobserveCalled++ - return this + var bindingsMock = { + test: { + root: true, + key: 'test', + value: { + __observer__: { + off: function () { + unobserveCalled++ + return this + } } + }, + unbind: function () { + bindingUnbindCalled = true } - }, - unbind: function () { - bindingUnbindCalled = true } } @@ -487,10 +481,6 @@ describe('UNIT: ViewModel', function () { assert.strictEqual(unobserveCalled, 3) }) - it('should not unbind external bindings', function () { - assert.notOk(externalBindingUnbindCalled) - }) - it('should remove self from parentCompiler', function () { var parent = compilerMock.parentCompiler assert.ok(parent.childCompilers.indexOf(compilerMock), -1)