文章列表
 
您正在查看 "python, webframework,django" 分类下的文章

2011年11月02日 1:38

利用simplejson和jQuery实现AJAX异步是非常容易的

step1,

def test(request):  # @url1

  ...

  return simplejson.dumps({'message':'Hello from Python!'})

step2,

<input id='test'/>

step3,

$.get(url1)

...

如此而已!!

 

但是作为程序员总希望创造自己的工具库,让上述步骤更通用、不重复。dajax就是这样的工具

官方主页在,http://dajaxproject.com/

该项目被分成两个部分 dajaxice 和 dajax。前者dajaxice实际上和上面提到的方法一样,只"前进"了一点点。用AJAX取回来的数据还要自己动手撰写js代码用于格式化到DOM组件上!

ajax.py

fromdjango.utils importsimplejson
fromdajaxice.decorators importdajaxice_register
 
@dajaxice_register
defdajaxice_example(request):
returnsimplejson.dumps({'message':'Hello from Python!'})

html

<inputname="rand"value="Get message from server!"id="rand"onclick="Dajaxice.examples.dajaxice_example(my_callback)"type="button">

javascript

functionmy_callback(data){
alert('Error');
}
 
dajax就进步了,我们可以完全在python(django)用类似jquery的风格自己决定客户端DOM组件的格式数据
fromdajax.core.DajaximportDajax
defassign_test(request):
dajax=Dajax()
dajax.assign('#block01 li','innerHTML','Something else...')
returndajax.json()

<divonclick="Dajaxice.app.assign_test(Dajax.process);">Click Here!</div>

Part II

django中除了上述的AJAX运用,其实我们还有很大一部分工作是Focus在Model的操作上。
这个github上也有现成的库- Django-ajax
主页 通过配置式的代码 非常容易就实现了Model的CRUD操作

 
2011年08月28日 16:09

 上篇我们讲到使用Simple History App是最便捷的,可是不能记录具体操作人的名字。那就改造她!

 

首先我们要获得当前用户的信息。在simple_history目录下创建middleware.py

try:
    from threading import local
except ImportError:
    from django.utils._threading_local import local

_thread_locals = local()
def get_current_user():
    return getattr(_thread_locals, 'user', None)

class ThreadLocals(object):
    """Middleware that gets various objects from the
    request object and saves them in thread local storage.
    """
    def process_request(self, request):
        _thread_locals.user = getattr(request, 'user', None)

注,有人反应这个发放对网站安全不好,不过考虑许多情况我们的应用是构架在内网的,忽略

 

修改simple_histrory的Model.py。SH使用的信号的方式触发行为的,

A,为history表添加用户名字段

    def create_history_model(self, model):
        """
        Creates a historical model to associate with the model provided.
        """
        attrs = self.copy_fields(model)
        attrs.update(self.get_extra_fields(model))
        attrs.update(Meta=type('Meta', (), self.get_meta_options(model)))
        attrs.update({'user':models.CharField(max_length=128)})  #ForeignKey(User)
        name = 'Historical_%s' % model._meta.object_name
        return type(name, (models.Model,), attrs)

 

B, 往数据表中添加用户名

    def create_historical_record(self, instance, type):
        manager = getattr(instance, self.manager_name)
        attrs = {}
        for field in instance._meta.fields:
            attrs[field.attname] = getattr(instance, field.attname)
        attrs['user'] = middleware.get_current_user().username
        manager.create(history_type=type, **attrs)
至此一个可以记录用户名的simple_history改造完成了

最后别忘了设置你的setting.py

MIDDLEWARE_CLASSES = (

    '.simple_history.middleware.ThreadLocals',

)

改造后的simple_history代码

 
2011年07月22日 15:39

为每个用户分配各个数据表的读、写、删除权限,其实不单单是企业开发需要,一般系统同样需要。但是在企业应用中我们还需要根据,即监督那些白名单中的用户行为。比如,有删除权限的用户,删除了某条销售记录,但是我想知道是谁干的...

下面,我就谈谈目前我采取的方法,

1/方法一 txt格式的log文件  
记录格式
2011-07-21 15:54:22,020 INFO views.customer_add Line:116>>User,ghiewa -Action,Add -Table,vendor_customer
2011-07-21 15:54:22,020 INFO views.customer_add Line:116>>User,ghiewa -Action,Add -Table,vendor_customer

 首先配置log,

若使用是Django1.3之前的版本需要配置 (在setting文件末尾添加)
root = logging.getLogger()
if len(root.handlers) == 0: pass #avoid conflict
level = logging.INFO
filename = os.path.join(PROJECT_ROOT,'log.log')
format = '%(asctime)s %(levelname)s %(module)s.%(funcName)s Line:%(lineno)d%(message)s'
hdlr = handlers.TimedRotatingFileHandler(filename,"midnight",1,90)
fmt = logging.Formatter(format)
hdlr.setFormatter(fmt)
root.addHandler(hdlr)
root.setLevel(level)

然后,

在views.py中
import logging
在具体model 操作语句后添加
logging.info('>>User,%s -Action,%s -Table,%s' %(request.user.username,'Add','vendor_contact'))

 --附--

若是1.3版本
import logging

# Get an instance of a logger
logger = logging.getLogger(__name__)

def my_view(request, arg1, arg):
    ...
    if bad_mojo:
        # Log an error message
        logger.error('Something went wrong!')

 

2/ 方法二、 使用simple_history APP (github.com上获取代码)
将改APP放置到python可视的目录下,比如我放置在当前工程的ext文件夹下
在Model定义文件中
from ext.simple_history.models import HistoricalRecords

class Test(models.Model):
     ...
     history = HistorialRecords()

如此即可

取点没有记录是谁  另会多出许多HistoryalXXX表


方法3,编写自己的代码 利用Django Admin的数据表
#core/history.py
from django.contrib.auth.models import User

from middleware import threadlocals
from django.contrib.contenttypes.models import ContentType
from django.contrib.admin.models import ADDITION
from django.contrib.admin.models import CHANGE
from django.contrib.admin.models import LogEntry
from django.db import models
from django.contrib.admin.models import DELETION
from django.db.models import signals
from django.db.models.base import ModelBase
import datetime

def prepare_fields(instance):
    output = {}
    class_fields = instance.__class__._meta.fields
    instance._history_fields = {}
    all = dict([(f.name, f) for f in class_fields])
    for field_name in instance._history['fields']:
        modelfield = all[field_name]
        value = getattr(instance, modelfield.attname)
        if value is None: value = ''
        output[field_name] = unicode(value)
    return output

def add_signals(cls):
    def post_delete(instance, **_kwargs):
        if instance._history.get('model', False):
            instance._create_log_entry(DELETION)

    def pre_save(instance, **_kwargs):
        if instance._history.get('fields', []):
            if instance.pk is None:
                instance._history_fields = {}
                for field_name in instance._history['fields']:
                    instance._history_fields[field_name] = ''
            else:
                try:
                    db_instance = instance.__class__.objects.get(pk=instance.pk)
                except instance.__class__.DoesNotExist:
                    db_instance = instance
                instance._history_fields = prepare_fields(db_instance)
        if instance._history.get('model', False):
            if instance.pk == None:
                instance._history_action = ADDITION
            else:
                instance._history_action = CHANGE
   
    def post_save(instance, **_kwargs):
        if instance._history.get('fields', []):
            pre_fields = instance._history_fields
            post_fields = prepare_fields(instance)
            for name, after in post_fields.iteritems():
                #print 'looking if', name, 'changed...'
                before = pre_fields[name]
                if before != after:
                    # field has been changed
                    #print 'changed', name, 'from', before, 'to', after
                    instance._create_field_log_entry(name, after)
            #print 'checking done.'
           
        if instance._history.get('model', False):
            instance._create_log_entry(instance._history_action)

    signals.pre_save.connect(pre_save, sender=cls, weak=False)
    signals.post_save.connect(post_save, sender=cls, weak=False)
    signals.post_delete.connect(post_delete, sender=cls, weak=False)

class ModelWithHistoryBase(ModelBase):
    def __new__(cls, name, bases, attrs):
        Model = ModelBase.__new__(cls, name, bases, attrs)
        history = getattr(Model, 'History', None)
        if history:
            history = history.__dict__
            if not 'model' in history:
                history['model'] = False
            if not 'fields' in history:
                history['fields'] = []
        else:
            #raise "Please add History subclass to your model"
            history = {
                'model': False,
                'fields': []
            }
        Model._history = history
        add_signals(Model)
        return Model

class ModelWithHistory(models.Model):
    __metaclass__ = ModelWithHistoryBase
    class Meta:
        abstract = True
       
    def _create_log_entry(self, action):
        if threadlocals.get_current_user().is_anonymous():
            user = User.objects.get(pk=0)
        else:
            user = threadlocals.get_current_user()
        history = LogEntry(user=user, object_id = self.pk, action_flag = action,
                            content_type = ContentType.objects.get_for_model(self))
        try:
            history.object_repr = repr(self)
        except Exception:
            history.object_repr = "(unknown)"
        history.save()

    def _create_field_log_entry(self, name, value):
        if threadlocals.get_current_user().is_anonymous():
            user = User.objects.get(pk=0)
        else:
            user = threadlocals.get_current_user()
        from core.models import AttributeLogEntry
        history = AttributeLogEntry(user=user, object_id = self.pk, field_name=name, field_value = value,
                            content_type = ContentType.objects.get_for_model(self))
        try:
            history.object_repr = repr(self)
        except Exception:
            history.object_repr = "(unknown)"
        history.save()

    def get_history(self):
        content_type = ContentType.objects.get_for_model(self)
        return LogEntry.objects.filter(object_id=self.pk, content_type=content_type)

    def has_history(self):
        return bool(self.__class__._history.get('model', False))
       
    def last_edited_at(self):
        history = list(self.get_history()[:1])
        if not history:
            return datetime.datetime(2000, 1, 1, 0, 0, 0)
        else:
            return history[0].action_time
       
    def last_edited_by(self):
        history = list(self.get_history()[:1])
        if not history:
            return User.objects.get(pk=1)
        else:
            return history[0].user

#core/models.py
from django.db import models
from django.contrib.auth.models import User
from django.contrib.contenttypes.models import ContentType
from django.utils.safestring import mark_safe
from django.contrib.admin.util import quote
from django.utils.encoding import smart_unicode
from django.utils.translation import ugettext_lazy as _
from django.utils.datetime_safe import strftime
import datetime

#almost dupe of LogEntry model
class AttributeLogEntry(models.Model):
    class Meta:
        verbose_name = _('attribute log entry')
        verbose_name_plural = _('attribute log entries')
        db_table = 'django_attribute_log'
        ordering = ('-action_time',)

    action_time = models.DateTimeField(_('action time'), auto_now=True)
    user = models.ForeignKey(User)
    content_type = models.ForeignKey(ContentType, blank=True, null=True)
    object_id = models.IntegerField(_('object id'), blank=True, null=True)
    field_name = models.CharField(_('field name'), max_length=200, blank=True, null=True)
    field_value = models.TextField(_('field value'), null=True, blank=True)
   
    def __repr__(self):
        return smart_unicode(self.action_time)

    @staticmethod
    def get_history(obj, field):
        content_type = ContentType.objects.get_for_model(obj)
        return AttributeLogEntry.objects.filter(object_id=obj.pk, field_name=field, content_type=content_type)

    def get_edited_object(self):
        "Returns the edited object represented by this log entry"
        return self.content_type.get_object_for_this_type(pk=self.object_id)

    def get_admin_url(self):
        """
        Returns the admin URL to edit the object represented by this log entry.
        This is relative to the Django admin index page.
        """
        return mark_safe(u"%s/%s/%s/" % (self.content_type.app_label, self.content_type.model, quote(self.object_id)))

    @staticmethod
    def last_edited_at(obj, field):
        history = list(AttributeLogEntry.get_history(obj, field)[:1])
        if not history:
            return None
        else:
            return strftime(history[0].action_time, "%Y-%m-%d %H:%M")
       
    @staticmethod
    def last_edited_by(obj, field):
        history = list(AttributeLogEntry.get_history(obj, field)[:1])
        if not history:
            return None
        else:
            return history[0].user


使用方法

class MyModel(ModelWithHistory):
    class History:
        model = True # save model changes into admin's LogEntry table
        fields = ('f1', 'f2') # save these fields history to AttributeLogEntry table
    f1 = CharField(max_length=100)
    f2 = IntegerField()

总结,

 比较后,你会发现方法二和方法三是用了类似的思路即自动记录,并讲结果记录在Database中。但是显然方法二的代码工作量更少,与本身系统的耦合度非常的低,是推荐方法。但是缺陷不能告诉我们是谁的动作

 

2011.08.28 15:43修改

 
2011年03月19日 21:26
经常有人问我,JavaScript应该怎么学。
这个问题其实很好回答:
  1. 先学基本语法,如果曾学过C等语言,应该1小时内就能掌握了。
  2. 再去使用内置的函数、方法和DOM API,熟悉它能干什么。
  3. 然后弄懂匿名函数和闭包,学会至少一个常用的JavaScript库(例如jQuery)。
  4. 最后领悟它的对象实现,尝试去扩展已有库,或编写自己的库。
可学习过程却并不像回答般轻松,因为国内的网站到处充斥着转载。那些人连自己都没弄懂的玩意就转载出来,甚至包含一些明显的错误和不堪入目的代码,却还有一大批小白惊呼“好强大,看不懂”。
在这种可悲的环境中,想要学好JavaScript还真棘手,因为你得有足够的经验来判断这篇文章是否值得一看,内容是否有错,哪些地方可以改进。
为了避免这种弯路,我还是自己写篇来整理整理吧。前2个阶段的就不提了,那是浪费你我的时间。

匿名函数
JavaScript中的函数定义很简单:
function 函数名(参数) {
// 函数体
}
当不写函数名的时候,它就是一个匿名函数了,只不过你没法通过函数名来引用它。

很显然,匿名函数也是函数,而函数是Function的实例(对象),它还可以用这种方式来创建:
new Function('参数名', '/* 函数体 */');
由于匿名函数是个对象,因此可以把匿名函数对象赋值给一个变量:
var 函数名 = function(参数) {
// 函数体
}
第1和第3段代码实现的效果是完全相同的,就是定义一个函数,并与“函数名”这个名称进行绑定。

当然,就算不与某个名称绑定,我们照样可以调用一个匿名函数:
(function(a, b) {
return a + b;
})(1, 2);
第一个括号内是匿名函数的定义,这个括号的作用是提高运算优先级,以便引用这个匿名函数对象;最后那个括号内则是调用它的实际参数,因此这段代码的作用就是将1和2作为参数传递给一个匿名函数,并返回结果3。
这样做有什么好处呢?很简单,在这个匿名函数里定义的变量名只在其内部有效,不会影响全局名字空间,不用担心命名冲突。

此外,我们还能利用arguments.callee来实现递归调用匿名函数,这就是一个计算斐波那契数列的实现:
(function (n) {
if (n <= 2) {
return 1;
}
return arguments.callee(n - 1) + arguments.callee(n - 2)
})(10);
和其他对象一样,函数可以作为参数传递给另一个函数,匿名函数也不例外:
function 高阶函数(低阶函数, 参数) {
return 低阶函数(参数) * 2;
}

高阶函数(function(x) {
return x + 1;
}, 3);
这段代码中,高阶函数的函数体里调用了低阶函数,就像C中的函数指针一样,所以应该不难理解。
重点是在调用高阶函数时,直接传递了一个匿名函数作为参数(不需要括号)。它实际上相当于:
var 匿名函数 = function(x) {
return x + 1;
}

高阶函数(匿名函数, 3);
所不同的是“匿名函数”这个变量名并不存在,而是在调用时直接作为一个函数对象传递给了高阶函数。


闭包
之前我也曾写过一篇《JavaScript的闭包》,但在这篇文章里,我想用更多的代码来解释。
正如前面所说,函数也是对象,那么在函数体内部定义一个函数对象是很合理的:
function f() {
function g() {
}
}

更进一步地,内部函数可以使用外部作用域的变量:
var a = 1;
var b = 2;
function f() {
var a = 3;
function g(c) {
var d = 4;
/*
g里定义了d,因此d为4
g的参数里有c,因此c为f传递给它的5
g里没有定义a,而外层的f定义了a,因此a为3
g和f里都没定义b,而全局名字空间里有b,因此b为2
顺带一提,如果连全局名字空间里都没有的话,那就是undefined了
*/

return a + b + c + d;
}
return g(5);
}

函数可以返回内部的变量给外部作用域,而函数对象本身也是个变量,因此也可以作为返回值:
function f() {
function g(x) {
return x + 1;
}
return g;
}

f()(2);
其中f()的值就是f函数内部的g函数,因此相当于调用的是g(2)。

更进一步地,这个内部函数也可以使用外部作用域的变量:
var a = 1;

function f(b) {
var c = 3;
function g() {
var d = 4;
return a + b + c + d;
}
c = 5;
return g;
}

var h = f(2);
h(); // 12

a = 6;
h(); // 17

f(7)(); // 22
这段代码很奇特,外部的h被赋值为f(2),因此f和g中的b都是2。那么c是多少呢?在定义g之前,它是3,可在定义之后又被重新赋值为5了。
实际上根本无需纠结,函数在定义时是不会去执行内部的代码的,只有在调用时才会代入这些变量的值。而在调用时,c已经变成了5,所以就以5计算了。同样的,更改a和b也会影响最终的结果。
而且,你甚至可以把g函数的定义提到变量c的声明之前,结果仍然是不变的,并不会出现c不存在的错误。(事实上很多JavaScript编译器就是这么做的。)

如果你看懂了上述这些代码,那么你其实已经弄懂闭包了。
实际上,当你在内部函数g里使用外部函数f的变量c时,为了能在f执行结束(return)后仍能引用它里面的c,c就不能被销毁,而是与g绑定在一起了。此处的变量c就被称为自由变量,而绑定了自由变量的函数g就被称为闭包。
另外,为了说明的方便,我将这个内部函数命名为g了,其实它也可以是匿名函数。

那么闭包有什么用呢?
举个很简单的例子:假如我的函数要用到一个值,这个值的计算很耗时间,但每次计算的结果实际上都一样。那么正常人肯定会先用一个变量保存这个计算结果,然后才去调用这个函数。
但是这个值我并不希望暴露到全局名字空间,那么就必须作为内部变量了。而让闭包去引用这个内部变量,一切就迎刃而解了。

注意,我不止一次地提到尽量不要将变量名暴露到全局名字空间,这样做的好处你慢慢会懂的。


对象
在JavaScript中,并非一切都是对象,例如null和undefined。此外还有1、0.0、NaN、true和"hello"等简单类型的对象,它们虽然也有方法和属性,但并不在我所要讨论的范畴中。
剩下的几乎全是对象,例如{}、[]、/1/、new Date()和function(){}等。这些对象的共同点是typeof的值是"object"或"function",且instanceof Object的值为true。(注意typeof(null)的值是"object",但null并没有方法和属性,因此我不认为它是对象。)
由此可见,JavaScript的对象都是Object的实例。

那么Object、对象和函数究竟是什么?
在解释之前前,我还是先上一段代码:
>>> typeof(Object)
"function"
>>> typeof((function(){}))
"function"
>>> (function(){}) instanceof Object
true
>>> (function(){}) instanceof Function
true
>>> typeof(new Object())
"object"
>>> (new Object()) instanceof Object
true
>>> (new Object()) instanceof Function
false
>>> Object instanceof Object
true
>>> Function instanceof Function
true
>>> Function instanceof Object
true
>>> Object instanceof Function
true
由于typeof(Object)的值是"function",并且Object还是Function的实例,因此它必然是个函数。
而所有的对象都是Object或其子类的实例,由此可得,所有的对象都是函数的实例,并且它们的类都是函数。这听上去很奇怪,但这也正是JavaScript与其他面向对象语言的不同点之一。

在大部分面向对象的语言中,类是对象的模板,一个类的所有实例都共享这个类的方法和属性;而与此同时,类还提供了构造器来初始化实例。
而在JavaScript中则正好相反:函数本身用来充当构造器,而与此同时,函数的原型(prototype)则被函数的实例所共享。
也就是说,当函数作为对象模板来使用时,它的实例是对象,而不要想当然地认为函数的实例就是函数(例如new Object()就不是)。

函数作为一种对象,它必然也是某个函数的实例,这个函数就是Function。
由于Function本身就是函数,所以它也是Function的实例。
再由于函数都是对象,Function也不例外,所以Function也是对象,因此它也是Object的实例。
而Object是函数,因此也是Function的实例。
此外Object还是对象,所以Object也是Object的实例。
这就是函数和对象之间乱七八糟的关系,看不懂也没关系,认真你就输了~

在你彻底凌乱之前,我先总结一下吧:
  • 对象是函数的实例。
  • 函数都是对象。
  • 函数都是Function的实例。
  • 对象都是Object的实例。
  • Object和Function都是Object和Function的实例。


接下来就揭开对象的神秘面纱吧。简单来说,对象就是一个字典:
var a = new Object();
a['x'] = 1;
a.y = 2;

a.x == 1;
a['y'] == 2;
new Object()是一个空对象(实际上就是{}),它是Object的实例。在这个例子中可以看到,当键名是一个合法的属性名时(不能以数字开头等),键值和这个对象的属性值是一回事。

再看看JavaScript是怎样自定义一个“类”的。
function 动物(名字) {
this.名字 = 名字;
this.叫什么 = function() {
return '我叫' + this.名字;
}
}

var 神马 = new 动物('神马');
神马.叫什么();
这里的new很关键,它表示将“动物”这个函数作为构造函数来初始化一个对象,而不仅仅是执行这个函数。具体来说,它会构造一个空对象{},然后将this指向这个对象,最后执行函数体。(其实还有一些其他的工作,这里先不提。)
如果没有new的话,就不会构造一个空对象,this也不会指向这个对象,而是指向全局对象(在浏览器里是window)。
因此,这段代码实际上相当于:
function 动物(名字) {
this.名字 = 名字;
this.叫什么 = function() {
return '我叫' + this.名字;
}
}

var 神马 = {};
动物.call(神马, '神马');
神马.叫什么();

顺便解释一下这里的call方法,它是将第一个参数作为this(如果没有的话就是null,于是会指向全局对象window),剩余的参数作为被调函数的参数来调用“动物”函数。
此外还有个类似的apply方法,举个例子应该就能弄懂了:
function f(a, b, c) {
return this + a + b + c;
}

f.apply(1, [2, 3, 4]); // 10
f.call('1', 2, 3, 4); // "1234"

你应该会注意到,用它可以让对象和方法分离,于是可以用其他对象来调用并不属于它的方法:
var a = {};
a[0] = 1;
a[1] = 2;
a[2] = 3;
a.length = 3;

Array.prototype.join.apply(a, ['']); // "123"
Array.prototype.join.call(a, ''); // "123"
在这个例子中,我模拟了一个包含3个数的数组,然后想把数组中所有的数连接成一个字符串。
如果是原生的Array对象的话,是有join方法的,可我模拟的数组却没有, 于是便借用了Array.prototype.join方法。

此外,构造函数的返回值也很重要,如果它没有返回值(即返回值为undefined),或返回1、'1'和true等简单类型的字面量对象,那么 new表达式的结果会是新构造出来的对象;但如果返回了一个对象(包括{}、new Number(1)和new String('1')等),那么new表达式的结果就是这个对象。
function A(x) {
this.x = x;
return x;
}

var a = new A(1);
a.x; // 1

a = new A({y: 2});
a.x; // undefined
a.y; // 2

原型
细心的话你应该会注意到我2次提到了prototype这个东西,却没有对其进行介绍。
实际上prototype是构造函数的一个属性。在new表达式初始化一个对象时,会将对象与构造函数的prototype相关联。当尝试访问对象的某个属性时,如果对象本身没有这个属性,那么会继续查找它的构造函数的prototype。
你有没有想过为什么所有的函数都有apply和call方法?这就是因为所有的函数都是Function的实例,而Function.prototype里定义了这2个方法。

接下来就给个例子:
function 动物(名字) {
this.名字 = 名字;
}

动物.prototype.叫什么 = function() {
return '我叫' + this.名字;
}

var 神马 = new 动物('神马');
神马.叫什么();

var 草泥马 = new 动物('草泥马');
草泥马.叫什么();

神马.叫什么 === 草泥马.叫什么; // true
这个例子和之前的很像,所不同的是我并没有定义“this.叫什么”,而是定义了“动物.prototype.叫什么”。而神马和草泥马本身并没有“叫什么”这个方法,于是在调用时,实际上是调用“动物.prototype.叫什么”。
这样的好处是不需要每个对象都重新定义一个自己实现来占用内存。此外,类属性也可以用prototype来实现。

了解了prototype的作用后,接下来就说说如何获取它吧。
在Firefox、Chrome和Safari等浏览器里,所有JavaScript对象都有个__proto__属性,这个属性就是它的构造函 数的prototype(很显然,new一个对象的时候还需要做这件事)。不过这个属性并不是JavaScript标准中所定义的,并且已被摒弃。标准中 建议的是使用Object.getPrototypeOf()方法,但这个方法是JavaScript 1.8.1才引入的,而且无法作为左值。
神马.__proto__ === 动物.prototype;
Object.getPrototypeOf(草泥马) === 动物.prototype;

另外,prototype还有个constructor属性,它默认指向构造函数自身。这个属性可以帮助我们知道一个对象是由哪个函数创建的。
神马.__proto__.constructor === 动物;
神马.constructor === 动物;
动物.prototype.constructor === 动物;

值得一提的是,__proto__和prototype都是可以动态更改的:
神马.__proto__ = {
'叫什么': function() {
return '我叫' + this.名字 + ',请多指教';
}
};

动物.prototype.吃什么 = function() {
return '我吃河蟹';
};

神马.叫什么(); // "我叫神马,请多指教"
神马.吃什么(); // TypeError: Object #<an Object> has no method '吃什么'
草泥马.叫什么(); // "我叫草泥马"
草泥马.吃什么(); // "我吃河蟹"

另外,prototype实际上也是一个对象,而这个对象也是有__proto__属性的,因此它还可以用来链式地实现继承。
function 动物(名字) {
this.名字 = 名字;
}

动物.prototype.叫什么 = function() {
return '我叫' + this.名字;
};

function(名字, 食物) {
动物.call(this, 名字); // 调用父类的构造函数
this.食物 = 食物;
}

马.prototype = new 动物(); // 需要复制动物.prototype到马.prototype
马.prototype.吃什么 = function() {
return '我吃' + this.食物;
};

var 神马 = new 马('神马', '草');
神马.叫什么(); // "我叫神马"
神马.吃什么(); // "我吃草"

var 草泥马 = new 马('草泥马', '河蟹');
草泥马.叫什么(); // "我叫草泥马"
草泥马.吃什么(); // "我吃河蟹"
这段代码有2处要说明。
第一处是“动物.call(this, 名字)”。还记得之前所说的new的意义吗?这里就是将马函数里的this作为动物函数的this来调用,用来初始化父类。
第二处是“马.prototype = new 动物()”。实际上我们也可以用“马.prototype = 动物.prototype”,可是这样一来,改写马的prototype时,动物的prototype也会被改变。而如果用“马.prototype = new Object(动物.prototype)”,动物和马的prototype混在了一起,就不方便分开,也无法体现继承关系了。所以这里new了一个动物 对象,这样就得到了它的__proto__属性。当要访问草泥马.叫什么时,草泥马并没有这个属性;于是查看草泥马.__proto__,发现仍然没有; 于是再检查草泥马.__proto__.__proto__(其实就是动物.prototype),终于发现了叫什么,于是便调用这个方法了。
这个做法当然是有缺点的,例如constructor属性就不正确(手动赋值即可修正),而且代码看上去很繁琐。要解决这个问题,最好的方法就是使用现成的JavaScript库,例如MooTools和Prototype等。

顺带一提,实际上instanceof就是依靠这样的原型链来判断一个对象是否是一个函数的实例的:
var temp = 马.prototype.__proto__;
草泥马.__proto__.__proto__ = {};
草泥马 instanceof 动物; // false
草泥马 instanceof 马; // true
马.prototype.__proto__ = temp;
草泥马 instanceof 动物; // true

草泥马.__proto__ = {};
草泥马 instanceof 马; // false

草泥马.__proto__ = 动物.prototype;
草泥马 instanceof 动物; // true
草泥马 instanceof 马; // false

var prototype = {'__proto__': 马.prototype};
prototype instanceof 动物; // true
prototype instanceof 马; // true

草泥马.__proto__ = prototype;
草泥马 instanceof 动物; // true
草泥马 instanceof 马; // true

我想你应该已经发现其中的一个陷阱了。
对象的__proto__属性是它的构造函数的prototype,而prototype也是对象,它的__proto__属性是 prototype的构造函数的prototype,也是个对象。这样一路追溯下去,由于对象肯定有__proto__属性,这就导致了查找可能会无限循 环下去。
因此Object.prototype这个对象很特殊,它的__proto__属性为null,因此当检查到它后,就停止继续查找了。由于所有的对象都是Object的实例,因此不必担心查询陷入死循环。

此外,下述代码也验证了Object和Function之间的关系(是否到此才恍然大悟呢):
Object.__proto__ === Function.prototype; // Object是Function的实例
Function.__proto__ === Function.prototype; // Function是Function的实例
Object.__proto__.__proto__ === Object.prototype; // Object是Object的实例
Function.__proto__.__proto__ === Object.prototype; // Function是Object的实例

({}).constructor === Object;
Object.constructor === Function;
(function(){}).constructor === Function;
Function.constructor === Function;
 
2011年03月12日 20:24

 最近老在思考这个问题,究竟什么才是Bottle想遵循的,倘若不是这样,为什么在她下面期待一个Session Auth等功能那么难 (当然您功力很深厚,外练神功 自行装配功能块 -- WSGI设计了就是干这个,那自然不必困扰) 

...

 肯能下面这个就是了

-QUOTE-

关于Unix哲学

作者: 阮一峰

先讲两个很老的小故事。

第一个故事。

有一家日本最大的化妆品公司,收到了用户的投诉。用户抱怨买来的肥皂盒是空的。这家公司为了防止再发生这样的事故,很辛苦地发明了一台X光检查器,能够透视每一个出货的肥皂盒。

同样的事故,发生在一家小公司。他们的解决方法是买一台强力的工业电扇,对着肥皂盒猛吹,被吹走的就是空肥皂盒。

第二个故事。

美国太空总署(NASA)发现在太空失重状态下,航天员无法用墨水笔写字。于是,他们花了大量经费,研发出了一种可以在失重状态下写字的太空笔。猜猜看,俄国人是怎么解决的?(答案在本文结尾处。)

=====================

这几天,我在看Unix,发现很多人在谈“Unix哲学”,也就是开发Unix系统的指导思想。

Wikipedia上列出了好几个版本,不同的人有不同的总结。发明管道命令的Doug McIlroy总结了三条,而Eric S. Raymond则在The Art of Unix Programming一书中,一口气总结了17条(英文版中文版)。

但是我发现,所有人都同意,“简单原则”——尽量用简单的方法解决问题——是“Unix哲学”的根本原则。这也就是著名的KISS(keep it simple, stupid),意思是“保持简单和笨拙”。

下面就是我对“简单原则”的笔记。如果你想最简单地完成一项编程任务,我认为可以从四个方面入手:

1. 清晰原则。

代码要写得尽量清晰,避免晦涩难懂。清晰的代码不容易崩溃,而且容易理解和维护。重视注释。不为了性能的一丁点提升,而大幅增加技术的复杂性,因为复杂的技术会使得日后的阅读和维护更加艰难。

2. 模块原则。

每个程序只做一件事,不要试图在单个程序中完成多个任务。在程序的内部,面向用户的界面(前端)应该与运算机制(后端)分离,因为前端的变化往往快于后端。

3. 组合原则。

不同的程序之间通过接口相连。接口之间用文本格式进行通信,因为文本格式是最容易处理、最通用的格式。这就意味着尽量不要使用二进制数据进行通信,不要把二进制内容作为输出和输入。

4. 优化原则。

在功能实现之前,不要考虑对它优化。最重要的是让一切先能够运行,其次才是效率。“先求运行,再求正确,最后求快。”(Make it run, then make it right, then make it fast.)90%的功能现在能实现,比100%的功能永远实现不了强。先做出原型,然后找出哪些功能不必实现,那些不用写的代码显然无需优化。目前,最 强大的优化工具恐怕是Delete键。

==================

答案是,俄国人用铅笔。

(完)

 
   
 
 
文章存档
 
     
 
最新文章评论
  

很及时,谢谢!
 

回复leader20:没有
 

这个跟django版本有关系吗?
 

装了n回,才找到这个
 

你爱的我也爱
   
帮助中心 | 空间客服 | 投诉中心 | 空间协议
©2012 Baidu