揭秘js框架中的常用套路

Ember Computed属性

可能现在大多数人一听到计算属性,首先想到的是 Vue 中的 Computed 计算属性。其实在 Ember 框架也提供了这样一个特性,用于计算属性的属性。有点绕口,看一个官方例子吧:

App.Person = Ember.Object.extend({

firstName: null,

lastName: null,

fullName: function() {

return this.get('firstName') + ' ' + this.get('lastName');

}.property('firstName', 'lastName')

});

var ironMan = App.Person.create({

firstName: "Kobe",

lastName: "Bryant"

});

ironMan.get('fullName') // "Kobe Bryant"

Person 对象具有firstName和lastName属性。computed属性fullName返回包含person全名的连接字符串。令人奇怪的地方在于fullName的函数使用了

.property 方法。 我们看一下 property 的代码:

Function.prototype.property = function() {

var ret = Ember.computed(this);

// ComputedProperty.prototype.property expands properties; no need for us to

// do so here.

return ret.property.apply(ret, arguments);

};

通过添加新属性调整全局函数对象的原型。在类定义期间运行一些逻辑是一种很好的方法。

Ember 使用 getter 和 setter 来操作对象的数据。这就简化了计算属性的实现,因为我们之前还有一层要处理实际的变量。但是,如果我们能够将计算属性与普通js对象一起使用,那就更有趣了。例如:

var User = {

firstName: 'Kobe',

lastName: 'Bryant',

name:

function() {

// getter + setter

}

};

console.log(User.name); // Kobe Bryant

User.name = 'LeBron James';

console.log(User.firstName); // LeBron

console.log(User.lastName); // James

name作为一个常规属性,本质上就是一个获取或设置firstName和lastName的函数。

JavaScript有一个内置的特性,可以帮助我们实现这个想法:

var User = {

firstName: 'Kobe',

lastName: 'Bryant',

};

Object.defineProperty(User, "name", {

get: function() {

return this.firstName + ' ' + this.lastName;

},

set: function(value) {

var parts = value.toString().split(/ /);

this.firstName = parts[0];

this.lastName = parts[1] ? parts[1] : this.lastName;

}

});

Object.defineProperty 方法可以接受对象、对象的属性名、getter 和 setter 。我们要做的就是编写这两个方法的实现逻辑。运行上面的代码,我们就能得到想要的结果:

console.log(User.name); // Kobe Bryant

User.name = 'LeBron James';

console.log(User.firstName); // LeBron

console.log(User.lastName); // James

Object.defineProperty 虽然是我们想要的,但显然我们不想每次都这么写。在理想的情况下,我们希望提供一个接口。在本节中,我们将编写一个名为 Computize 的函数,它将处理对象并以某种方式将name函数转换为具有相同名称的属性。

var Computize = function(obj) {

return obj;

}

var User = Computize({

firstName: 'Kobe',

lastName: 'Bryant',

name: function() {

...

}

});

我们想使用name方法作为setter,同时作为getter。这类似于Ember的计算属性。

现在,我们将自己的逻辑添加到函数对象的原型中:

Function.prototype.computed = function() {

return { computed: true, func: this };

};

这样就可以在每个Function定义后直接调用computed函数了。

name: function() {

...

}.computed()

name属性不再是一个函数,而变成一个对象: { computed: true, func: this } 。其中 computed 等于true, func属性指向原本的函数。

真正神奇的事情发生在Computize helper的实现中。它遍历对象的所有属性,对所有的计算属性使用object.defineproperty:

var Computize = function(obj) {

for(var prop in obj) {

if(typeof obj[prop] == 'object' && obj[prop].computed === true) {

var func = obj[prop].func;

delete obj[prop];

Object.defineProperty(obj, prop, {

get: func,

set: func

});

}

}

return obj;

}

注意: 我们将计算属性name删除了,原因是Object.defineProperty在某些浏览器下仅对未定义的属性起作用。

下面是使用.computed()函数的用户对象的最终版本:

var User = Computize({

firstName: 'Kobe',

lastName: 'Bryant',

name: function() {

if(arguments.length > 0) {

var parts = arguments[0].toString().split(/ /);

this.firstName = parts[0];

this.lastName = parts[1] ? parts[1] : this.lastName;

}

return this.firstName + ' ' + this.lastName;

}.computed()

});

函数的逻辑就是,判断是否有参数,如果有参数就直接将参数进行分割处理,并分别为firstname和lastname赋值,最终返回完整的名字。

结束

在大型框架和库的背后包含着许多优秀前辈的经验。通过学习这些框架能够让我们更好理解这些框架背后的原理,能够脱离框架开发,这点很重要。

原文:http://www.ajiehome.com/2018/08/09/jie-mi-jskuang-jia-zhong-de-chang-yong-tao-lu/

揭秘js框架中的常用套路


分享到:


相關文章: