从另一个实例更新实例的属性

时间:2017-06-24 01:01:03

标签: javascript object inheritance

我正在寻找在对象实例中更新的最佳方式,例如本例ko.bindingHandlers.checkbox = { init: function(element, valueAccessor, allBindingsAccessor) { var values = valueAccessor(); var checked = ko.utils.unwrapObservable(values.checked); var css = ko.utils.unwrapObservable(values.css); var $element = $(element); var button = $('<button type="button" class="btn btn-check ' + (values.buttonClass || '') + '" data-toggle="buttons-checkbox"><i class="' + (values.iconClass || 'icon-ok') + '"></i> ' + (values.text || '') + '</button>'); button.click(function (evt, data) { if (typeof (values.callback) != 'undefined') { values.callback($(element).is(':checked') || checked); } return true; }); button.insertBefore(element); }, update: function(element, valueAccessor, allBindingsAccessor) { var values = valueAccessor(); var checked = ko.utils.unwrapObservable(values.checked); if (typeof(values.callback) != 'undefined') { values.callback($(element).is(':checked') || checked); } } }; 的name属性。我理解实现这一点的唯一方法是将父对象的引用作为参数传递给构造函数中的myParentObjectsnew myChildObj(this,name)实例的方法,如{{ 1}}。

我无法想象一个子对象嵌套4-5级别,并且必须更新它的父级传递myChildObj它的参数的属性,这将是一个管理的噩梦!必须有更好的方法来更新父属性!

myChildObj.updateParentProperty(name)

问题:更新父对象参数的最佳方法是什么?

2 个答案:

答案 0 :(得分:6)

让我感到困惑的是:

this.init=()=>{
   this.names.forEach((name)=>{
      var childObj = new myChildObj(this,name);
      childObj.updateParentProperty();
   })
}

因为只要创建了所有三个孩子,你的父母就会有一个名为 cassie 的名字(因为这是最后一个孩子叫updateParentProperty)。这和你的标签是我倾向于声称你正在寻找经典继承的原因,如下所示:

经典继承

&#13;
&#13;
function myParentObj (name) {
    this.name = name;
    this.names = ['jordan','danny','cassie'];
    this.init=()=>{
       this.names.forEach((name)=>{
          var childObj = new myChildObj(name);
          console.log(childObj);
       });
    }
}

function myChildObj (name) {
	myParentObj.call(this, name);
  
  // Other stuff, only available for myChildObj
}

function init(){
     var parentObj = new myParentObj();
     parentObj.init();
}
document.addEventListener('DOMContentLoaded',init);

var parent = new myParentObj();
console.log(parent);
&#13;
&#13;
&#13;

这意味着孩子从其父级继承属性,但当他们自己的名字发生变化时,当然不会为父级更新它们。

一些简单的观察者模式

好的,您的示例只有一个属性,应该注意更改。 我认为下面例子中的观察者模式有点过分,但对于较大的应用程序,它非常有用。。观察者模式与JavaScript不同(参见enter link description here)。基本上,您有一个对象,应该监视更改( observable )以及一个或多个想要根据这些更改执行操作的对象( observers )。实现可以非常简单(如我的演示)或非常复杂。基本思想是观察者维持其观察者名单。该示例使用自己的简单实现。还有可用的库,我们使用knockout,它为您的页面提供MVVM结构,甚至可以根据更改更新DOM。

&#13;
&#13;
function myParentObj(){
    this.name = 'jordan'
    this.names = ['jordan','danny','cassie'];
    this.init=()=>{
       this.names.forEach((name)=>{
          var childObj = new myChildObj(name);
          childObj.subscribe('name', newValue => this.name = newValue);
       })
    }
}
function myChildObj(name){
    this.subscribers = {};
    
    Object.defineProperty(this, 'name', {
      set: newValue => {
        this._name = newValue;
        this.notify('name');
      },
      get: () => this._name
    });
    
    this.subscribe = function (prop, callback) {
      if (!this.subscribers.hasOwnProperty(prop)) {
        this.subscribers[prop] = [];
      }
      
      this.subscribers[prop].push(callback);
    };
    
    this.notify = function (prop) {
      if (this.subscribers[prop]) {
        this.subscribers[prop].forEach(callback => callback(this[prop]));
      }
    };
}
function init(){
     var parentObj = new myParentObj();
     parentObj.init();
}
document.addEventListener('DOMContentLoaded',init);

var parent = new myParentObj();
var child = new myChildObj('bob');
child.subscribe('name', newName => parent.name = newName);
console.log(parent.name);
child.name = 'cassie';
console.log(parent.name);
&#13;
&#13;
&#13;

使用Object.assign

您还可以使用Object.assign,它会将所有可枚举属性从一个对象复制到另一个对象。但是,您应该注意,它将复制所有属性,即使是那些您可能不想更改的属性。

&#13;
&#13;
function myParentObj(){
    this.name = 'jordan'
    this.names = ['jordan','danny','cassie'];
    this.init=()=>{
       this.names.forEach((name)=>{
          var childObj = new myChildObj(this,name);
          childObj.updateParentProperty();
       })
    }
}
function myChildObj(name){
    this.name = name;
}

var parent = new myParentObj();
var child = new myChildObj('cassie');
Object.assign(parent, child);
console.log(child);
&#13;
&#13;
&#13;

答案 1 :(得分:2)

经典继承

我看到你在某些ES6中混合了,所以你可能正在寻找使用ES6 standards of object-oriented inheritance这样的答案:

class Parent {
  constructor(name = 'jordan') {
    this.name = name
    this.names = ['jordan', 'danny', 'cassie']
  }
  
  init() {
    return this.names.map((name) => {
      return new Child(name)
    })
  }
}

class Child extends Parent {
  constructor(name) {
    super(name)
    
    // other stuff for Child
  }
}

let parent = new Parent()
console.log(parent)
let children = parent.init()
children.forEach((child) => console.log(child))

在ES5中,inheritance was usually implemented like this代替,虽然这不是完全 ES6 class所做的事情,因为ES6做的事情就像让实例方法不可枚举等等。

function Parent(name) {
  name = arguments.length > 0 ? name : 'jordan'

  this.name = name
  this.names = ['jordan', 'danny', 'cassie']
}

// member methods
Parent.prototype = {
  init: function init() {
    return this.names.map(function (name) {
      return new Child(name)
    })
  }
}

function Child(name) {
  // basically, super(name)
  Parent.call(this, name)
  
  // other stuff for Child
}


// extending Parent
Child.prototype = Object.create(Parent.prototype)
Parent.prototype.constructor = Parent

var parent = new Parent()
console.log(parent)
var children = parent.init()
children.forEach(function (child) { console.log(child) })

下面几乎就是第一个例子中ES6代码的ES5等价物:

function Parent() {
  var name = arguments.length > 0 && name !== undefined ? name : 'jordan'

  this.name = name
  this.names = ['jordan', 'danny', 'cassie']
}

// member methods
Object.defineProperties(Parent.prototype, {
  init: {
    configurable: true,
    value: function init() {
      return this.names.map(function (name) {
        return new Child(name)
      })
    },
    writable: true
  }
})

function Child(name) {
  // basically, super(name)
  Parent.call(this, name)
  
  // other stuff for Child
}


// extending Parent
Child.prototype = Object.create(Parent.prototype, {
  constructor: {
    configurable: true,
    value: Child,
    writable: true
  }
})

var parent = new Parent()
console.log(parent)
var children = parent.init()
children.forEach(function (child) { console.log(child) })

EventEmitter

在查看下面另一个答案的评论后,您似乎对观察者类型模式最感兴趣。 EventEmitter,在Node.js中原生定义,是观察者模式中使用最广泛的实现。下面是客户端JavaScript的polyfill演示:

class Parent extends EventEmitter {
  constructor(name = 'jordan') {
    super()

    this.name = name
  }
}

class Child {
  constructor(parent, name = parent.name) {
    this.parent = parent
    this._name = name
  }

  set name(name) {
    console.log('setting child name')

    this._name = name

    this.parent.emit('name', name)
  }
  
  get name() {
    return this._name
  }
}

let parent = new Parent()

parent.on('name', function (name) {
  console.log('setting parent name')

  this.name = name
}.bind(parent))

let child = new Child(parent)

console.log('parent', parent.name)
console.log('child',  child.name)

child.name = 'danny'

console.log('parent', parent.name)
console.log('child',  child.name)
<script src="https://cdn.rawgit.com/mudge/5830382/raw/a4bc230f5bce01ea9a34b0d42247256531b97945/eventemitter.js"></script>

代理

JavaScript提供的另一个简洁的API称为Proxy,它允许元编程,即重新定义JavaScript在单独情况下的工作方式:

var parent = {}
var child = {}

var proxy = new Proxy(child, {
  set: (target, property, value) => {
    // target === child
    target['child-' + property] = typeof value + ' ' + value
    parent['parent-' + property] = typeof value + ' ' + value
    
    return value
  }
})

proxy.name = 'jordan'
proxy.age = 54

console.log(parent)
console.log(child)