The concept of objects in JavaScript can indeed be confusing. Look at the following example:
var strPrimitive = "I'm mamacat"; typeof strPrimitive; // "string" strPrimitive instanceof String; // false var strObject = new String("I'm mamacat"); typeof strObject; // "object" strObject instanceof String; // true strPrimitive.substr(8, 3); // "cat"
The same string is assigned to an object. Sometimes it is a string type and sometimes it is an object. However, variables that are obviously not object types can still use object attributes. Why is this?
[Related course recommendations: JavaScript video tutorial]
Types and built-in objects
There are six main types in JavaScript (Language) types, namely string, number, boolean, null, undefined and object, among which the first five basic types are not objects (typeofing null results in "object", which is the language itself BUG). Beyond this, there are many special object subtypes, such as arrays, functions, and built-in objects.
The names of some built-in objects look the same as simple basic types, such as String, Boolean, Object and so on. In terms of expression, these built-in objects are similar to the concept of "class" in other object-oriented languages. As mentioned in the previous article, they are actually just some built-in functions that can be used to construct a corresponding subtype (don't be confused) , functions are also objects, there is no contradiction here). So we can go back to the original example. strObject is a variable constructed by the built-in function/built-in object String, corresponding to the String subtype, so it is an object, and strPrimitive is just a primitive literal value.
Of course, at the bottom of the above example, we seem to have called the substr() function on strPrimitive. This is because the JavaScript engine will convert the original literal into the corresponding object when needed, and after the conversion We can naturally use attributes to access the corresponding methods.
Object properties
So, as far as the above example is concerned, the String object instance will have the substr() function available, but according to the previous article, we can know that These "functions" themselves do not belong to an object, but these functions are essentially an attribute of the corresponding object. Of course, even if we say that a certain type of object itself has various attributes, in fact these attributes mostly exist independently, but are just related together in the form of references. This is not inconsistent with what we have learned before. . These related things are called properties of the object.
Copying of Objects
Insert a quick report. Although mentioned in the previous article, it has been repeatedly emphasized above that attributes are only independent entities that are related in the form of references. Yes, we sometimes still "take it for granted" that attributes are part of the object, and one of the easiest places to get into trouble is the copying of objects. If you think about it carefully, you will know that when we copy an object, since its attributes themselves are only reference associations, the attribute references contained in the "copied" object actually point to the same location as the attribute references of the original object:
var ori = { a : 1}; var ori_copy = ori; ori.a = 61; ori_copy.a; // 61
Obviously this may be different from our expectations, and if we want to truly copy the object, there is no perfectly applicable solution. In many cases, the common practice is to serialize the object and then deserialize it. Get a new object to copy the object (such as using json). Object.assign()
was added in ES6 to perform a shallow copy of the object. The method is to assign all enumerable properties of the object to the new object with an equal sign. However, it should be noted that the equal sign assignment does not assign the meta-information of the attribute (attribute descriptor, described later), so you should pay special attention to it if necessary.
Attribute access and array
The way to access the properties associated with the object is through the . or [] operator, obj.a and obj["a" ] The attributes accessed are essentially the same, and the only difference between these two forms of access is whether there can be strange symbols in the names of the attributes accessed. What is thrown in the [] operator is a string. In fact, the attribute name is always a string. Of course, what may be surprising about this concept is that subscript access to arrays is not an exception. Numbers are still converted into strings before they are used.
// 对象的属性访问: var tejilang = {1 : "Teji Wolf"}; tejilang instanceof Array; // false tejilang["1"]; // "Teji Wolf" tejilang[1]; // "Teji Wolf" // 这回保证它是 Array var macat = ["codingcat"]; macat instanceof Array; // true macat.length; // 1 macat[0]; // "codingcat" macat["0"]; // "codingcat" macat.length = 20; macat; // (20) ["codingcat", empty × 19]
数组下标既然不属例外情况,那数组对象必然有其它属性控制数组本身的行为,例如上例中,macat 数组的长度就是 length 属性所体现的,通过修改它的值也就改变了对象本身对外的表现形式。当然,由于数组本身就是对象,所以我们还是可以把数组当键值对来用,只是这种做法通常是没有意义且会让人感到困惑的。JavaScript 引擎通常都根据对象的类型做了不同程度的优化,故除了代码逻辑可读性外,合理的使用也是多少可以改善性能的。
能够通过字符访问属性还是存在一些别的好处的,比如 ES6 的可计算属性名。当然 ES6 不在本文的关注范围内,所以这里就不再讨论了。
属性描述符
有时我们可能不希望某个属性被随意修改,有时候我们需要额外配置一些属性的信息,自 ES5 起,所有的属性就都具备了“属性描述符”(Property Descriptor)来控制属性本身的这些元信息。
数据描述符
来看这个例子:
var chris = {}; Object.defineProperty(chris, "IQ", { value: 228, writable: false, configurable: true, enumerable: true }); chris.IQ = 61; // 静默失败了,如果是严格模式则会 TypeError chris.IQ; // 228
通过 defineProperty 可以对一个对象的属性配置其对应的属性描述符(元信息),而属性描述符则包含访问描述符和数据描述符,上面的例子中,defineProperty 的第三个参数就定义了数据的若干数据描述符,其中 writable 表示可写,configurable 表示属性是否可配置(注意,修改成不可配置是单向操作),enumerable 则表示属性是否应当出现在枚举中,比如 for..in 中。
显然我们可以通过属性描述符实现对属性的保护,而同时也存在一些方便函数来做近似的事。如 Object.preventExtensions() 会保留原有属性但禁止添加新属性,Object.seal() 会密封对象,在禁止添加新属性的基础上把原有属性标记为不可配置,Object.freeze() 会冻结对象,即在密封的基础上把数据访问属性标记为不可写。
[[Get]], [[Put]] 和访问描述符
在我们访问和赋值一个对象的属性时,实际上是通过 [[Get]] 和 [[Put]] 操作进行的,例如属性访问时,[[Get]] 会先找有没有这个属性,如果没有则会遍历对象的 [[Prototype]] 链(原型链,这次不谈这个概念)来找,实在找不到则返回 undefined 。而这个行为实际是允许我们通过设置 getter (get())和 setter (set())函数来改变的,它们被称为 访问描述符。
当我们提供访问描述符时,对应的访问操作就不再受到 value 和 writable 属性的影响了,另外需要注意的是,尽管它们也是属性描述符,但定义 getter 和 setter 并不要求一定要通过 defineProperty 设置:
var obj = { get a() { // 给 a 属性定义 getter return this._a_; }, set a(val) { // a 属性的 setter this._a_ = val * 2; } } obj.a = 2; obj.a; // 4
属性存在性
因为属性的值也可能是 undefined,不存在的属性直接访问得到的也是 undefined,所以直接通过简单的属性访问是无法区分是否存在的,这时我们即可通过 in 或者 hasOwnProperty() 检查属性是否存在对象中了:
var obj = {a : 2}; "a" in obj; // true obj.hasOwnProperty("a"); // true
尽管仍没有讲到原型链的概念,这里仍然应注意,in 操作符会检查原型链中是否存在属性,而 hasOwnProperty 则不会。另外在一些情况下,有的对象会没有 hasOwnProperty 这个属性(此处不提原因),这时可以用过 Object.prototype.hasOwnProperty.call(objName, propertyName) 来实现检查。
本文来自 js教程 栏目,欢迎学习!
The above is the detailed content of Detailed explanation of object properties in JavaScript. For more information, please follow other related articles on the PHP Chinese website!