一、用toString判断类型
toString() 方法返回一个代表该对象的字符串。当对象需要转换为字符串时,会调用它的toString()方法。
默认情况下,每个对象都会从Object上继承到toString()方法,如果这个方法没有被这个对象自身或者更接近的上层原型上的同名方法覆盖(遮蔽),则调用该对象的toString()方法时会返回"[object type]",这里的字符串type表示了一个对象类型。点击查看在线代码:
var toString = Object.prototype.toString; function write_one(str) { document.writeln('<p>'+str+'</p>'); } // [object Date] write_one(toString.call(new Date)); // [object String] write_one(toString.call(new String)); // [object Math] write_one(toString.call(Math)); // [object Number] write_one(toString.call(1)); // [object Boolean] write_one(toString.call(true)); // [object Object] write_one(toString.call(new Object())); // [object Regexp] write_one(toString.call(/\d/)); // [object Array] write_one(toString.call([])); // [object Function] write_one(toString.call(write_one)); // [object Error] write_one(toString.call(new Error())); // [object Arguments] function display_args() { write_one(toString.call(arguments)); }; display_args(); //Since JavaScript 1.8.5 // [object Undefined] write_one(toString.call(undefined)); // [object Null] write_one(toString.call(null));
开源的underscore库中的那几个isFunction、isString等方法就是用toString做的判断:
_.each(['Arguments', 'Function', 'String', 'Number', 'Date', 'RegExp', 'Error'], function(name) { _['is' + name] = function(obj) { return toString.call(obj) === '[object ' + name + ']'; }; });
二、基本类型
《JavaScript语言精髓与编程实践》中提到有6种基本类型:
write_one(typeof 1); //number write_one(typeof 'xx'); //string write_one(typeof true); //boolean write_one(typeof undefined); //undefined write_one(typeof null); //object write_one(typeof function(){}); //function
三、对象
对象是JavaScript中的基本数据结构。JavaScript支持继承,即通过动态代理机制重用代码或数据。但不像许多传统的语言,JavaScript的继承机制基于原型,而不是类。
1)对象的类型
1.本地对象:
ECMA-262 把本地对象(native object)定义为“独立于宿主环境的 ECMAScript 实现提供的对象”。包括如下:
Object、Function、Array、String、Boolean、Number、Date、RegExp、Error、EvalError、RangeError、ReferenceError、SyntaxError、TypeError、URIError
2.内置对象:
ECMA-262 把内置对象(built-in object)定义为“由 ECMAScript 实现提供的、独立于宿主环境的所有对象,在 ECMAScript 程序开始执行时出现”。这意味着开发者不必明确实例化内置对象,它已被实例化了。
3.宿主对象:
ECMAScript中的“宿主”当然就是我们网页的运行环境,即“操作系统”和“浏览器”。所有的BOM和DOM对象都是宿主对象。
2)早绑定和晚绑定
所谓绑定(binding),即把对象的接口与对象实例结合在一起的方法。
早绑定(early binding)是指在实例化对象之前定义它的属性和方法,这样编译器或解释程序就能够提前转换机器代码。在 Java 和 Visual Basic 这样的语言中,有了早绑定,就可以在开发环境中使用 IntelliSense(即给开发者提供对象中属性和方法列表的功能)。ECMAScript 不是强类型语言,所以不支持早绑定。
晚绑定(late binding)指的是编译器或解释程序在运行前,不知道对象的类型。使用晚绑定,无需检查对象的类型,只需检查对象是否支持属性和方法即可。ECMAScript 中的所有变量都采用晚绑定方法。这样就允许执行大量的对象操作,而无任何惩罚。
晚绑定示例如下,点击查看在线代码:
var a=function(){}; a.prototype.p1 = 1; var b = new a(); write_one(b.p1);//1 write_one(b.p2);//undefined a.prototype.p2=2; write_one(b.p2);//2
- b初始化a的一个实例,此时输出b.p1,根据prototype链接,结果是1
- 而第一次输出b.p2时,显示的 undefined
- 然后,再设置a.prototype.p2=2,此时在输出b.p2,显示的是2
- 所有这些就是prototype链在作怪
3)new操作符
JavaScript的所有函数都可以作为构造器。构造器与普通的方法没有什么区别。
prototype存在于每一个函数上,当new一个函数的时候,这个实例对象就拥有这个函数的prototype对象的一切成员。
从而实现共享一组方法或属性。这跟火影里的影分身应该差不多,分身也会螺旋丸技能,思维也是共通的等。
function naruto1() {} naruto1.prototype = { say: "never give up", SpiralPill: function() {}//螺旋丸 }; var a = new naruto1; var b = new naruto1; write_one(a.say === b.say);//true write_one(a.SpiralPill === b.SpiralPill);//true
4)特权方法与特权属性
直接在构造器内指定其方法,叫特权方法,如果是属性就叫特权属性。每一个实例一个副本,各不影响。
这就比如各个影分身的表情、动作等,可以各不相同的
特权属性和方法是会遮住原型方法或属性的。
这就好比分身用了风车手里剑后就不能使用螺旋丸了。
function naruto1() { this.say = "I want to become Naruto"; this.SpiralPill = function() { return 'windmill hand sword';//风车手里剑 } this.expression = function(){} } naruto1.prototype = { say: "never give up", SpiralPill: function() {}//螺旋丸 }; var a = new naruto1; var b = new naruto1; write_one(a.say === b.say);//true write_one(a.SpiralPill === b.SpiralPill);//false write_one(a.expression === b.expression);//false,引用类型每次进入函数体都重新创建,因此不一样
5)类方法与类属性
原型方法和特权方法都属于实例方法。类方法或属性可直接定义在函数上。
naruto1.bag = function(){}; var c = new naruto1; write_one(c.bag === undefined);//true
6)继承
鸣人的螺旋丸是从自来也那里学来的,后续他自己又开发了新的螺旋丸相关的忍术。
function Jiraiya(){}//自来也 Jiraiya.prototype = { SpiralPill: function() {}//螺旋丸 }; function bridge(){}//修行 bridge.prototype = Jiraiya.prototype; function naruto2(){} naruto2.prototype = new bridge(); naruto2.prototype.windmill = function(){ //风车手里剑 独有 } var a = new Jiraiya(); var b = new naruto2(); write_one(a.SpiralPill === b.SpiralPill);//true write_one(Jiraiya.prototype === naruto2.prototype);//false write_one(a.windmill === b.windmill);//false write_one(b instanceof Jiraiya);//true write_one(b instanceof naruto2);//true
7)__proto__
在标准浏览器和IE11中,可以访问__proto__属性。
var a = new naruto();
分解步骤:
- var a={}; 也就是说,初始化一个空对象a
- a.__proto__ = naruto.prototype
- 将构造器里的this=a
- 执行构造器里的代码
8)ECMA-262,第5版中将对象增强
1.Object.create():
使用一个指定的原型对象和一个额外的属性对象创建一个新对象。
2.Object.preventExtensions() :
阻止将任何新命名的特性添加到对象
3.Object.isExtensible():
测试命名特性是否可以添加到对象
4.Object.seal():
首先调用Object.preventExtensions()
,阻止添加新特性,然后将所有对象特性的configurable
标志设置为 false。这允许锁定对象的所有特性(但不锁定对象的值),使其可预测,以用作数据存储。循环一个密封的数据对象的值现在成为一个更加可预测的任务,特别是配合使用 enumerable
标志时。
5.Object.isSealed() :
测试对象是否可配置
6.Object.freeze():
调用 Object.seal()
来停止对象的配置, 然后将所有对象特性的 writeable
标志设置为 false,提供一个完美静态的对象。在有多个源与数据交互的环境中,完全冻结对象的能力能创建一种此前无法实现的可预测性和安全性级别。
7.Object.isFrozen() :
测试对象是否冻结
8.Object.getPrototypeOf() :
返回对象的原型。该方法的值等同于非标准的 Object.__proto__ 特性。
9.Object.keys() :
方法返回一个字符串数组,表示一个对象自己的可数(enumerable)特性的名称,这些特性是在 enumerable 标志设置为 true 的正在接受调查的对象上直接定义的特性。
10.Object.getOwnPropertyNames() :
与上述方法类似,但还包含 enumerable 标志设置为 false 的特性。
11.访问符(getters 和 setters):
Get 和 set 将一个对象特性绑定到一个函数,该函数将在试图访问或写入一个属性的值时被调用。Get 不接受参数;而 set 接受一个参数(要设置的值)。