js起源
在编程语言中,抽象描述对象有原型和类两种方式,例如C++,java就是使用“类”的方式来描述对象,而JavaScript早年选择了原型。后来因为被要求模仿Java,就在“原型运行时”的基础上引入了new、this、等语言特性。
在ES6之前,大量的JavaScript程序员试图在原型体系的基础上,把JavaScript变得更像基于类的语言,进而产生了很多框架,比如Dojo。
JavaScript对象的特征
不管什么样的编程语言,对象都有下面3个本质特征:
- 对象具有唯一标识性:看起来相同的两个对象,并非同一个对象(因为唯一标识的内存地址)
- 对象有状态:同一对象可能处于不同状态之下
- 对象具有行为:对象的状态可能因为行为发生变迁
var o1 = { a: 1 }; var o2 = { a: 1 }; console.log(o1 == o2); // false
在JavaScript中统一将状态和行为抽象为“属性”。 和其他编程语言一样,JavaScript中的对象都很好的表现了对象的基本特征:标示性、状态和行为。
但是在JavaScript独有的特色是:对象具有动态性,也就是我们可以在运行时为对象添加属性。
JavaScript对象的两类属性
1.数据属性
- value:就是属性的值。
- writable:决定属性能否被赋值。
- enumerable:决定 for in 能否枚举该属性。
- configurable:决定该属性能否被删除或者改变特征值。 2.访问器属性
- getter:函数或 undefined,在取属性值时被调用。
- setter:函数或 undefined,在设置属性值时被调用。
- enumerable:决定 for in 能否枚举该属性。
- configurable:决定该属性能否被删除或者改变特征值。 可以用内置函数:getOwnPropertyDescriptor来查看 改变属性特征,或者定义访问器,可以使用object.defineProperty
因为JavaScript区别于主流编程语言一个比较重要的部分:JavaScript是基于原型的方式来实现面向对象的,而像C++,java等基于类来实现面向对象。那么原型的概念就很重要了。
JavaScript的原型
在我的认知里,每个实例上都有一个__proto__的属性(这个属性只是各家浏览器规定的,并不是一种规范),指向构造函数,而几乎每一个构造函数上都有私有字段prototype,指向该构造函数的原型,原型上除了你挂载的属性外,还有constructor,指回构造函数。
早期版本中的类与原型
在早期的JavaScript 版本中,“类”的定义是一个私有属性[[class]],内置类型Number,String等都指定了[[class]]属性。我们唯一访问[[class]]属性的方式是Object.prototype.toString。下面是所有具有内置class属性的对象。
var o = new Object;
var n = new Number;
var s = new String;
var b = new Boolean;
var d = new Date;
var arg = function(){ return arguments }();
var r = new RegExp;
var f = new Function;
var arr = new Array;
var e = new Error;
console.log([o, n, s, b, d, arg, r, f, arr, e].map(v => Object.prototype.toString.call(v)));
//(10) ["[object Object]", "[object Number]", "[object String]", "[object Boolean]", "[object Date]", "[object Arguments]", "[object RegExp]", "[object Function]", "[object Array]", "[object Error]"]
在ES5中,[[class]]私有属性被Symbol.toStringTag替代。我们可以使用Symbol.toStringTag来自定义Object.prototype.toString的行为。
var o = { [Symbol.toStringTag]: "MyObject" }
console.log(o + "");//[object MyObject]
因为new运算符是JavaScript面向对象的一部分,所以new运算符具体做了哪些事情是我们需要了解的:
- 以构造器的prototype属性为原型,创建新对象。
- 将this和调用参数传给构造器,执行构造器代码。
- 如果构造器返回的是对象,则返回;否则返回第一步创建的对象。
JavaScript中对象的分类
对象可以分为:
- 宿主对象:最常见的宿主就是浏览器环境和node环境。
- 内置对象:由JavaScript语言提供的对象。 内置对象又包含:
- 固有对象:由标准规定,随着 JavaScript 运行时创建而自动创建的对象实例。
- 原生对象:可以由用户通过 Array、RegExp 等内置构造器或者特殊语法创建的对象(通过构造器,用new创建的新对象)。
- 普通对象:由{}语法、Object 构造器或者 class 关键字定义类创建的对象,它能够被原型继承。
另外一种角度看对象分类
对象还可以被分为函数对象和构造器对象,任何对象只需要具有[[call]]私有字段,就可以作为函数被调用;如果具有[[construct]],就可以作为构造器使用。
同时用户用function关键字创建的函数必定同时是函数和构造器,不过,它们表现出来的行为效果却并不相同。而在ES6之后,()=>语法创建的函数仅仅是函数,无法被当成构造器使用。