gpt4 book ai didi

javascript - 奇怪的 `bind` 行为

转载 作者:行者123 更新时间:2023-11-29 16:07:09 24 4
gpt4 key购买 nike

我正在尝试创建一个类似于 React 的 createClass 函数的函数。它应该接受一个 POJO 并将其转换为构造函数,该函数可以使用附加参数调用。

代码如下:

function createClass(obj) {
return function(args = {}) {
Object.keys(obj).forEach(k => this[k] = obj[k]);
Object.keys(args).forEach(k => this[k] = args[k]);
Object.keys(this.actions).forEach(k => this.actions[k] = this.actions[k].bind(this));
console.log('binded to', this);
}
}

const MyClass = createClass({
actions: {
foo: function() {
console.log('actions.foo');
this.request();
}
},
foo: function() {
console.log('foo');
this.request();
}
});

const req1 = function() {
console.log('req1 called')
}

const req2 = function() {
console.log('req2 called')
}

const c1 = new MyClass({request: req1});
const c2 = new MyClass({request: req2});

// As expected
c1.request();
c2.request();
console.log('---')

// As expected
c1.foo();
c2.foo();
console.log('---')

// Error: both call req1
c1.actions.foo();
c2.actions.foo();
console.log('---')

我不明白为什么调用 c2.foo() 会按预期工作,但调用 c2.actions.foo() 却从另一个实例调用方法。怎么可能?

此外,there is jsbin example

最佳答案

因为在这条线上:

Object.keys(obj).forEach(k => this[k] = obj[k]);

...您正在将 actions(引用,而不是数组)复制到新对象。稍后您更新该数组的内容,但它们都共享同一个数组。由于您在 action 中的函数上调用 bind,它们将在第一次 时间绑定(bind)到第一个实例;随后,他们不会,因为 bind 的全部要点是它忽略了你调用它的 this,所以调用 bind绑定(bind)函数不执行任何操作(关于 this)。例如:

var f = function() { console.log(this.name); };
var f1 = f.bind({name: "first"});
f1(); // "first"
var f2 = f1.bind({name: "second"});
// ^^------ Note we're using an already-bound function
f2(); // Also "first"

因此,任何通过 actions 调用的函数都将始终使用 this 引用您创建的 first 实例来调用这些函数。

解决方案:复制数组内容(至少是浅拷贝),例如使用 Object.assign

这是删除了一些测试但检查 c1.actions === c2.actions 的代码,表明它是 true(它们使用相同的数组):

function createClass(obj) {
return function(args = {}) {
Object.keys(obj).forEach(k => this[k] = obj[k]);
Object.keys(args).forEach(k => this[k] = args[k]);
Object.keys(this.actions).forEach(k => this.actions[k] = this.actions[k].bind(this));
//console.log('binded to', this);
}
}

const MyClass = createClass({
actions: {
foo: function() {
//console.log('actions.foo');
this.request();
}
},
foo: function() {
//console.log('foo');
this.request();
}
});

const req1 = function() {
console.log('req1 called')
}

const req2 = function() {
console.log('req2 called')
}

const c1 = new MyClass({request: req1});
const c2 = new MyClass({request: req2});

console.log(c1.actions === c2.actions); // true

下面是复制actions的代码:

function createClass(obj) {
return function(args = {}) {
Object.keys(obj).forEach(k => {
if (k === "actions") {
this[k] = Object.assign({}, obj[k]);
} else {
this[k] = obj[k];
}
});
Object.keys(args).forEach(k => this[k] = args[k]);
Object.keys(this.actions).forEach(k => this.actions[k] = this.actions[k].bind(this));
//console.log('binded to', this);
}
}

例子:

function createClass(obj) {
return function(args = {}) {
Object.keys(obj).forEach(k => {
if (k === "actions") {
this[k] = Object.assign({}, obj[k]);
} else {
this[k] = obj[k];
}
});
Object.keys(args).forEach(k => this[k] = args[k]);
Object.keys(this.actions).forEach(k => this.actions[k] = this.actions[k].bind(this));
//console.log('binded to', this);
}
}

const MyClass = createClass({
actions: {
foo: function() {
console.log('actions.foo');
this.request();
}
},
foo: function() {
console.log('foo');
this.request();
}
});

const req1 = function() {
console.log('req1 called')
}

const req2 = function() {
console.log('req2 called')
}

const c1 = new MyClass({request: req1});
const c2 = new MyClass({request: req2});

// As expected
c1.request();
c2.request();
console.log('---');

// As expected
c1.foo();
c2.foo();
console.log('---');

// Error: both call req1
c1.actions.foo();
c2.actions.foo();
console.log('---');

或者您可以概括并始终复制非函数对象:

function createClass(obj) {
return function(args = {}) {
Object.keys(obj).forEach(k => {
const src = obj[k];
if (Array.isArray(src)) {
this[k] = src.slice();
} else if (typeof src === "object") {
this[k] = Object.assign({}, src);
} else {
this[k] = src;
}
});
Object.keys(args).forEach(k => this[k] = args[k]);
Object.keys(this.actions).forEach(k => this.actions[k] = this.actions[k].bind(this));
//console.log('binded to', this);
}
}

例子:

function createClass(obj) {
return function(args = {}) {
Object.keys(obj).forEach(k => {
const src = obj[k];
if (Array.isArray(src)) {
this[k] = src.slice();
} else if (typeof src === "object") {
this[k] = Object.assign({}, src);
} else {
this[k] = src;
}
});
Object.keys(args).forEach(k => this[k] = args[k]);
Object.keys(this.actions).forEach(k => this.actions[k] = this.actions[k].bind(this));
//console.log('binded to', this);
}
}

const MyClass = createClass({
actions: {
foo: function() {
console.log('actions.foo');
this.request();
}
},
foo: function() {
console.log('foo');
this.request();
}
});

const req1 = function() {
console.log('req1 called')
}

const req2 = function() {
console.log('req2 called')
}

const c1 = new MyClass({request: req1});
const c2 = new MyClass({request: req2});

// As expected
c1.request();
c2.request();
console.log('---');

// As expected
c1.foo();
c2.foo();
console.log('---');

// Error: both call req1
c1.actions.foo();
c2.actions.foo();
console.log('---');

我可能没有涵盖那里的所有边缘情况,您必须决定是否迎合(比如)RegExp 或 Date 对象等。

关于javascript - 奇怪的 `bind` 行为,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/38367547/

24 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com