面试官告诉我,我必须遵循javascript“模式”并编写“干净的代码”。他还说我应该遵循原型模式。这是我的代码示例:
//namespace declrations
var MyNamespace = {};
MyNamespace.UCs = {};
MyNamespace.Pages = {};
//function declarations
MyNamespace.UCs.test = function () { alert('this is a test function in user control namespace.'); }
MyNamespace.Pages.test = function () { alert('this is a test function in web page namespace.'); }
所以有人能指出为什么这段代码不合适?我的意思是,我首先声明了名称空间,然后添加了我的成员和函数,如上面的示例。它是真的有问题还是我错过了什么?
答案 0 :(得分:4)
当您在大型环境中编写代码时,很多问题都可能开始发生。因此,将类定义与使用这些类的方式分开非常重要。这也意味着你必须制作可以进行单元测试的课程,以证明他们按照你所说的去做。 Javascript不是一种真正的面向对象的语言,因此有几种方法来假装"假的"它。但由于语言具有很大的灵活性,我们可以复制一些方法。
我们想要远离的一件事是一种叫做功能范围的东西,因为它可能会导致意想不到的特征"后来,当其他3或4个程序员开始对你的代码做了什么做出假设。如果他们不知道全局变量在一两个函数闭包之前被覆盖了,那么它将使得发现该问题变得更加困难。因此,我建议使用由John Resig创建的小类,因为它提供了一种非常简单的方法,可以为您提供所需的大量功能。
所以让我们写一些代码。
var myNamespace = myNamespace || { }
/**
* Used to store a single entry in the log
*
* @class
*/
var myNamespace.LogEntry = Class.extend({
/**
* Used to track the beginning of the page load
*
* @private
* @static
*/
PAGE_LOAD_TIME = new Date().getTime(),
/**
* Used to store the current time
*
* @type int
*/
time : undefined,
/**
* The message of this log entry
*
* @type string
*/
msg : undefined,
/**
* @constructor
*
* @param {string} msg The message of this log entry
*/
init : function (msg) {
this.time = new Date().getTime() - this.PAGE_LOAD_TIME;
this.msg = msg
},
/**
* Displays this log entry in a single string
*
* @return {string} String representation of this log entry
*/
toString : function () {
return this.time + ": " + this.msg;
}
});
/**
* Used to store a log entry that has data associated with it.
*
* @class
* @extends myNamespace.LogEntry
*/
var myNamespace.DataEntry = myNamespace.LogEntry.extend({
/**
* Used to store data associated with this log entry
*
* @type object
*/
data : undefined,
/**
* @constructor
*
* @param {string} msg The message that describes this log entry
* @param {object} data The data associated with this entry
*/
init : function (msg, data) {
this._super(msg);
this.data = data;
},
/**
* @return {string} The string representation of this log entry
*/
toString : function () {
// Uses a JSON library to stringify the data into a json string.
return this._super() + JSON.stringify(this.data);
}
});
/**
* Provides an interface to log messages
*
* @class
*/
var myNamespace.Log = Class.extend({
/**
* Stores log entries
*
* @type myNamespace.LogEntry[]
*/
log : undefined,
/**
* @constructor
*/
init : function () {
this.log = [ ];
},
/**
* Logs a message into the log
*
* @param {string} msg The message you want to log
*/
msg : function (msg) {
this.log.push(new myNamespace.LogEntry(msg));
},
/**
* Log a message and data into the log
*
* @param {string} msg The message of this log entry
* @param {object} data The data associated with this log entry
*/
data : function(msg, data) {
this.log.push(new myNamespace.DataEntry(msg, data));
}
});
好的,这里有很多东西。主要部分是这是所有类的定义。我实际上并没有在那里使用任何东西。该程序仅将当前时间存储在LogEntry.PAGE_LOAD_START中,该时间被声明为@static,因此行为将是预期的。我在这里使用了很多jsDoc来清楚地说明了意图是什么。如果您没有按照您记录的方式使用这些类,那么像intelliJ这样的程序可以使用这些程序来反馈您的代码。
此程序将允许您创建和存储可能包含日志记录数据的日志条目。还有很多其他方法可以做到这一点。我在构造函数之前声明了所有内容,而不是在构造函数内部,因此我可以记录类型以及它们是否是私有的。
必须使用日志的程序员将确切知道如何使用它,如果他们想要创建或扩展这些类,他们可以这样做,而不会出现功能关闭带来的意外影响。
以下是如何使用它:
var anotherNamespace = anotherNamespace || {};
var anotherNamespace = new myNamespace.Log();
...
anotherNamespace.log.msg("This is a test");
...
anotherNamespace.log.data("Test msg with data", data);
当然,这里缺少的显而易见的事情是显示所有数据的方法。但这可能是在另一个迭代Log.log数组的类中,并将toString()吐出到网页或文件中。这里的要点是类和它们的功能很简单,可单元测试,只有定义。
答案 1 :(得分:1)
首先,利用对象文字,所有这些分配都是废话。
其次,你没有在任何地方实现原型“模式”,如果你完全控制命名空间,我也会去做一些封装:
(function() { // anonymous wrapper
function Foo(a) { // i can haz prototype "Pattern"
this.memberVal = a;
}
Foo.prototype = {
someMethod: function(value) {
console.log(value, this.memberVal);
},
yam: function() {
}
};
// Let's assume we have full control here, otherwise Alnitak's answer is fine too.
window.namespace = {
test: {
Foo: Foo
}
};
})();
var test = new namespace.test.Foo(123);
test.someMethod('bla');
答案 2 :(得分:0)
也许面试官字面意思是你应该使用'命名空间'方法?
使用示例的教程: http://elegantcode.com/2011/01/26/basic-javascript-part-8-namespaces/
答案 3 :(得分:0)
如果意图只是将实用程序函数放在共享命名空间中,而没有任何面向对象或数据封装,那么您的代码(大多数)看起来很好。
我会在初始名称空间声明中进行一次更改:
var MyNamespace = MyNamespace || {};
MyNamespace.UCs = MyNamespace.UCs || {};
MyNamespace.Pages = Mynamespace.Pages || {};
确保代码不会删除这些名称空间中的任何现有方法。