JavaScript语言精粹
JavaScript只有一个单一的数字类型(Number)。它在内部被表示为64位的浮点数,和Java的double一样。不像大多数其他的编程语言,它没有分离出整数类型,所以1和1.0是相同的值。这提供了很大的方便,因为它完全避免了短整数的溢出问题,并且你需要知道的关于数字的一切就是它是一种数字。这样就避免了一大类因数字类型导致的错误。
原型连接只有在检索值的时候才会被用到。如果我们尝试去获取对象的某个属性值,且该对象没有此属性名,那么JavaScript会试着从原型对象中获取属性值。如果那么原型对象也没有该属性,那么再从它的原型中寻找,以此类推,直到该过程最后到达终点Object.prototype. 如果属性完全不在原型链中的话,那么返回undefined值。这个过程称为委托(delegation)。但是 `hasOwnProperty` 不会查询原型链,只会在当前对象中查找属性。
在JavaScript中函数就是对象。对象是Name/Value对的集合并拥有一个连接到原型对象的隐藏链接。对象字面量产生的对象连接到Object.prototype. 函数对象连接到Function.prototype(该原型对象本身连接到Object.prototype,另外还有Array.prototype, String.prototype). 每个函数在创建时附有两个附加的隐藏属性:函数的上下文和实现函数的行为代码。
函数调用的4中模式:
- method invocation: `obj.call()` 然后obj绑定到this关键字上。
- function invocation: `add(1,2)` this绑定在全局对象window上。
- constructor invocation: `new ClassA` 这里ClassA是一个函数并且要求首字母大写,必须使用new运算符。
- `var p = new ClassA()` 那么 `p.__proto__
=
ClassA.prototype` - 然后ClassA.prototype里面有一个 `construtor` 属性 ` ClassA.prototype.constructor
=
ClassA` - 最后 `ClassA instanceof Function`
- `var p = new ClassA()` 那么 `p.__proto__
- apply invocaition: `funcObj.apply(ctx, parameters)` 其中ctx绑定函数体中的 `this` ,这个可以改变执行上下文。
关于prototype和__proto__这个问题可以在 https://www.zhihu.com/question/34183746 里面找找
function ClassA() { } console.log(ClassA instanceof Function) console.log(ClassA.__proto__ == Function.prototype) console.log(ClassA.prototype.constructor === ClassA) a = new ClassA() console.log(a.__proto__ === ClassA.prototype) var b = [1,2,3,4] console.log(b.__proto__ === Array.prototype) var c = "hello, world" console.log(c.__proto__ === String.prototype)
模块模式(module pattern)的一般形式是:一个定义了私有变量的和函数的函数;利用闭包创建可以访问私有变量和函数的特权函数;最后返回这个特权函数,或者是把他们保存在可以访问到的地方。
function moduleA() { var status = 10 var obj = { get_status: function() { return status; }, set_status: function(v) { status = v; } } return obj }
使用constructor invocation这种函数调用模式来模拟类(书里面成为伪类,pseudoclassical)存在许多问题:
- 没有私有环境,所有属性是公开的;无法访问super父类(调动父类的构造函数也不方便)
- 调用构造器函数时如果忘记增加new前缀,那么this会绑定到全局对象上,污染全局变量,而且没有任何警告和错误
- 这是一个严重的语言设计错误,为了降低这个问题带来的风险,所有构造器函数名约定首字母大写。
- 在基于类的语言中,类的继承是代码重用的唯一方式,而JavaScript则有更好的选择。
更好的方式应该是使用类似模块模式,下面是具体的模板可以参考,然后构造函数参数最好使用object而不是参数列表。另外可以参考文章 https://wangdoc.com/javascript/oop/prototype.html
// 这两个函数增加父类函数查找方法 Function.prototype.method = function(name, func) { if (!this.prototype.hasOwnProperty(name)) { this.prototype[name] = func } else { console.log("name already existed", name) } } Object.method('superior', function(name) { var that = this var method = this[name] return function() { return method.apply(that, arguments) } }) // 构造函数直接返回对象,对象内部有各种方法 // 这些方法通过闭包实现,来包装私有变量 function createAnimal(spec) { var age = spec.age var that = {} function get_age() { return age } that.get_age = get_age return that } animal = createAnimal({age: 10}) console.log(animal.get_age()) function createCat(spec) { var name = spec.name var that = createAnimal(spec) function get_name() { return name } that.get_name = get_name super_get_age = that.superior('get_age') // 获得父类方法 function get_age() { return "age = " + super_get_age() + ", name = " + get_name() } that.get_age = get_age return that } cat = createCat({age:10, name:"kitty"}) console.log(cat.get_age())
精简的JavaScript里面都是好东西,包括以下主要内容:
- 函数是头等对象:函数是有词法作用域的闭包。
- 基于原型继承的动态对象:对象是无类别的。我们可以通过普通的赋值给任何一个对象增加一个新成员元素,可以从另外一个对象继承成员元素。
- 对象字面量和数组字面量:对创建对象和数组非常方便,也是JSON的灵感来源。