JavaScript中的Objects

2024-05-27 05:18
文章标签 java script objects

本文主要是介绍JavaScript中的Objects,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

JavaScript中的Objects

在JavaScript中,创建Objects有两种方式,declarative (literal) form和 constructed form。

首先,声明式如下:

var myObj = {key: value// ...
};

构造式如下:

var myObj = new Object();
myObj.key = value;

声明式与构造式创建的对象完全相同,只不过构造式的需要一个个地添加添加属性,一般情况下我们都是采用声明式的创建对象。

JavaScript中的原始类型

JavaScrip中有六种原始类型:

  • string
  • number
  • boolean
  • null
  • undefined
  • object

他们并不属于对象类型,值得注意的是,null有时候被认为是对象类型,主要原因是typeof null的返回值为object(该语言的bug),实际上,null属于原始类型。

还有一种误解

everything in JavaScript is an object

显然这也是错误的。

对象类型中还有一些对象类型的子类型的( object sub-types),function是其子类型(专业术语称之为callable object),在JavaScript被认作是第一等的(first class),相比一般对象,function具有调用属性(callable behavior),还有一种对象类型的子类型为数组(Arrays ),相比一般对象,其内容更结构化。

JavaScript中还有一些内置对象的子类型,我们称之为内置对象(built-in objects),有如下几种:

  • String
  • Number
  • Boolean
  • Object
  • Function
  • Array
  • Date
  • RegExp
  • Error

这些内置对象表现出来的特性甚至与Java中的class的特性一致,实际上,它们都是内置函数(built-in functions),这些内置函数都被用作构造器(constructor ),关于JavaScript 中的构造器,

传统的面向对象的语言中,构造器属于class的特殊方法,当我们使用new实例化一个类时,其构造器被调用,JavaScript 中的构造器与此很不同,JavaScript 中的构造器只是一般的函数(functions ),其恰好在函数前面使用了new操作符,它们与class无关,也不是用来实例化一个类,它们只是一个很普通的函数,只不过被new操作符 控制 调用了而已,它们不是constructor functions,只是construction calls of functions而已。

var strPrimitive = "I am a string";
typeof strPrimitive;                            // "string"
strPrimitive instanceof String;                 // falsevar strObject = new String( "I am a string" );
typeof strObject;                               // "object"
strObject instanceof String;                    // true// inspect the object sub-type
Object.prototype.toString.call( strObject );    // [object String]

strPrimitive属于原始类型,strObject 属于对象类型,当我们如果要检查strPrimitive的长度,获取其内容时,我们需要String类型的对象类完成操作。

幸运的是,当我们将原始类型的string转化(coerces )为对象类型的String时,这些操作都被自动完成了,因此,一般来说,我们并不需要创建对象类型的变量,只需创建其对应的原始类型的变量即可,在需要时,JavaScript会自动帮我们转化为对应的对象类型。

var strPrimitive = "I am a string";console.log( strPrimitive.length );         // 13console.log( strPrimitive.charAt( 3 ) );    // "m"

null 和undefined没有相应的对象类型,Date 类型没有对应的原始类型,Object, Array, Function, 和RegExp,无论我们使用literal还是使用constructor方式构建,其均为对象类型。

Error对象我们一般很少创建,其都是在出现异常时自动创建。

一般来说,对象中的内容不一定存储在对象内部,其主要依赖于浏览器的实现,很多情况下,他们并不存储在对象容器中,真正存储在对象中的是对象的属性名,他们主要用来作为引用指向他们对应的值的地方。

var myObject = {a: 2
};myObject.a;     // 2myObject["a"];  // 2

使用 . 操作符的其名称与使用[""]的区别是后者可以使用UTF-8/unicode兼容的属性名,如[“Super-Fun!”],而前者则不可以通过. 操作符获得对应的属性值。

对象的属性名都是string类型,我们可以通过变量的值来作为指向对象属性值的地址:

var wantA = true;
var myObject = {a: 2
};var idx;if (wantA) {idx = "a";
}// laterconsole.log( myObject[idx] ); // 2

由于对象的属性名都是sting类型,如果不是string类型,JavaScript首先将其转化为string类型,我们在使用时要注意这一点:

var myObject = { };myObject[true] = "foo";
myObject[3] = "bar";
myObject[myObject] = "baz";myObject["true"];               // "foo"
myObject["3"];                  // "bar"
myObject["[object Object]"];    // "baz"

ES6中我们可以使用myObject[..]这种语法来计算出对象的属性名:

var prefix = "foo";var myObject = {[prefix + "bar"]: "hello",[prefix + "baz"]: "world"
};myObject["foobar"]; // hello
myObject["foobaz"]; // world

这种计算对象属性名主要是用在Symbol(new primitive data type which has an opaque unguessable value, technically a string value)上,对象中的函数同样也是不属于对象本身,其获取与属性的获取没有什么不同。

对于数组,我们需要注意的是,当我们向数组中添加属性时,如果该属性是数字类型的话,那么将作为数组的元素添加到数组中,而不是像一般的属性添加到数组对象中去:

var myArray = [ "foo", 42, "bar" ];myArray["3"] = "baz";myArray["a"]="a";myArray.length; // 4myArray[3];     // "baz"myArray.a;      //"a"

对象的Writable,Configurable,Enumerable等属性

ES5中,对象的属性可以通过 property descriptor来进行描述:

var myObject = {a: 2
};Object.getOwnPropertyDescriptor( myObject, "a" );
// {
//    value: 2,
//    writable: true,
//    enumerable: true,
//    configurable: true
// }

对象的属性有三个特征:writable, enumerable, 和configurable,他们的默认值如上例所示。我们可以Object.defineProperty(..)来为对象新添加一个属性,或者修改对象属性(如果是configurable的):

var myObject = {};Object.defineProperty( myObject, "a", {value: 2,writable: true,configurable: true,enumerable: true
} );myObject.a; // 2

当writable=false,相当于其对象没有setter方法:

var myObject = {};Object.defineProperty( myObject, "a", {value: 2,writable: false, // not writable!configurable: true,enumerable: true
} );myObject.a = 3;myObject.a; // 2

如果在strict mode下,则会报错:

"use strict";var myObject = {};Object.defineProperty( myObject, "a", {value: 2,writable: false, // not writable!configurable: true,enumerable: true
} );myObject.a = 3; // TypeError

configurable=false是单向操作,不可逆:

var myObject = {a: 2
};myObject.a = 3;
myObject.a;                 // 3Object.defineProperty( myObject, "a", {value: 4,writable: true,configurable: false,    // not configurable!enumerable: true
} );myObject.a;                 // 4
myObject.a = 5;
myObject.a;                 // 5Object.defineProperty( myObject, "a", {value: 6,writable: true,configurable: true,enumerable: true
} ); // TypeError

需要注意的一点是,当configurable=false时,我们可以配置writable的值从true到false,但是不能从false到true。

configurable=false还有一个作用就是阻止我们使用delete操作符删除该属性。

var myObject = {a: 2
};myObject.a;             // 2
delete myObject.a;
myObject.a;             // undefinedObject.defineProperty( myObject, "a", {value: 2,writable: true,configurable: false,enumerable: true
} );myObject.a;             // 2
delete myObject.a;
myObject.a;             // 2

对于enumerable,主要是用来控制for..in循环中是否可显示。

var myObject = { };Object.defineProperty(myObject,"a",// make `a` enumerable, as normal{ enumerable: true, value: 2 }
);Object.defineProperty(myObject,"b",// make `b` NON-enumerable{ enumerable: false, value: 3 }
);myObject.b; // 3
("b" in myObject); // true
myObject.hasOwnProperty( "b" ); // true// .......for (var k in myObject) {console.log( k, myObject[k] );
}
// "a" 2

我们可以调用propertyIsEnumerable来判断属性是否可枚举:

var myObject = { };Object.defineProperty(myObject,"a",// make `a` enumerable, as normal{ enumerable: true, value: 2 }
);Object.defineProperty(myObject,"b",// make `b` non-enumerable{ enumerable: false, value: 3 }
);myObject.propertyIsEnumerable( "a" ); // true
myObject.propertyIsEnumerable( "b" ); // falseObject.keys( myObject ); // ["a"]
Object.getOwnPropertyNames( myObject ); // ["a", "b"]

in 和 hasOwnProperty(..)的区别在于是否往上追溯[[Prototype]]链。

Object.keys(..) 和Object.getOwnPropertyNames(..)都只寻找该对象中的属性,不追溯[[Prototype]]链。

如果我们配置对象的属性为writable:false ,configurable:false,那么该属性不能被重新定义,不能改变,也不能delete。

var myObject = {};Object.defineProperty( myObject, "FAVORITE_NUMBER", {value: 42,writable: false,configurable: false
} );delete myObject.FAVORITE_NUMBER;console.log(myObject.FAVORITE_NUMBER);//42

如果我们防止继续往对象中添加新属性,使用preventExtensions即可:

var myObject = {a: 2
};Object.preventExtensions( myObject );myObject.b = 3;
myObject.b; // undefined

如果在strict mode下,会报TypeError错。

如果我们让对象不仅不能添加新的属性,而且还不能修改原来对象的属性,使用Seal即可,其首先调用preventExtensions(),然后再将对象属性配置为configurable:false。

freeze()是最高级别的让对象中的属性不可变,其先调用seal()方法,再将其属性的writable置为false。

注意一点,preventExtensions,Seal,Freeze都不会影响其对象中引用对象中属性的writable,configurable,enumerable的值。

[[Get]]和[[Put]]

var myObject = {a: 2
};myObject.a; // 2

上例中myObject.a,其能够获取到值实际上是执行了[[Get]]操作,[[Get]] 首先检查对象是否有a属性,如果有,那么将其对应的值返回。如果没有找到,则去对象的[[Prototype]]去寻找,直到Object,如果还没有找到,则返回undefined。

var myObject = {a: undefined
};myObject.a; // undefinedmyObject.b; // undefined

针对上述情况,我们没有办法区分对象中是否含有a属性或b属性。那么我们可以调用hasOwnProperty来判断是否含有该属性:

var myObject = {a: 2
};("a" in myObject);              // true
("b" in myObject);              // falsemyObject.hasOwnProperty( "a" ); // true
myObject.hasOwnProperty( "b" ); // false

in操作符检查该对象中或者[[Prototype]]链上是否含有该属性,而hasOwnProperty(Object中的方法)只检查该对象上是否含有钙属性,不向上追溯。

如果我们创建的对象不继承自Object(如Object.create(null)),那么调用hasOwnProperty()会报错,我们可以通过Object.prototype.hasOwnProperty.call(myObject,”a”)来调用hasOwnProperty方法。

注意一点,in是检查属性名是否存在于对象中,并不检查属性值是否在对象中。

4 in [2, 4, 6]//false

对于[[Put]],如果该对象有该属性,则

1.如果对象中含有accessor descriptor(即下文的Getters & Setters),调用该setter
2.对象的data descriptor中的writable是否为false,如果是,那么在non-strict mode下修改失败,不报错,在strict mode下报TypeError 。
3.其余情况下,则做正常修改。

如果对象没有该属性,情况会更复杂。

accessor descriptor:Getters & Setters

我们使用Getters & Setters可以覆盖对象属性默认的[[Put]] 和[[Get]]操作,当我们定义了对象属性的Getters & Setters时,则该属性的value 和writable失效。

var myObject = {// define a getter for `a`get a() {return 2;}
};Object.defineProperty(myObject,   // target"b",        // property name{           // descriptor// define a getter for `b`get: function(){ return this.a * 2 },// make sure `b` shows up as an object propertyenumerable: true}
);myObject.a; // 2myObject.b; // 4

上例中我们在对象上创建了两个属性,但是并没有定义其value,但是返回了我们定义的getter中的值。

var myObject = {// define a getter for `a`
    get a() {return 2;}
};myObject.a = 3;myObject.a; // 2

我们硬编码将a的返回值设置为2,故其默认[[Put]]操作无效,一般情况下我们都要设置其getter和setter方法:

var myObject = {// define a getter for `a`
    get a() {return this._a_;},// define a setter for `a`
    set a(val) {this._a_ = val * 2;}
};myObject.a = 2;myObject.a; // 4

这篇关于JavaScript中的Objects的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1006596

相关文章

Java五子棋之坐标校正

上篇针对了Java项目中的解构思维,在这篇内容中我们不妨从整体项目中拆解拿出一个非常重要的五子棋逻辑实现:坐标校正,我们如何使漫无目的鼠标点击变得有序化和可控化呢? 目录 一、从鼠标监听到获取坐标 1.MouseListener和MouseAdapter 2.mousePressed方法 二、坐标校正的具体实现方法 1.关于fillOval方法 2.坐标获取 3.坐标转换 4.坐

Spring Cloud:构建分布式系统的利器

引言 在当今的云计算和微服务架构时代,构建高效、可靠的分布式系统成为软件开发的重要任务。Spring Cloud 提供了一套完整的解决方案,帮助开发者快速构建分布式系统中的一些常见模式(例如配置管理、服务发现、断路器等)。本文将探讨 Spring Cloud 的定义、核心组件、应用场景以及未来的发展趋势。 什么是 Spring Cloud Spring Cloud 是一个基于 Spring

Javascript高级程序设计(第四版)--学习记录之变量、内存

原始值与引用值 原始值:简单的数据即基础数据类型,按值访问。 引用值:由多个值构成的对象即复杂数据类型,按引用访问。 动态属性 对于引用值而言,可以随时添加、修改和删除其属性和方法。 let person = new Object();person.name = 'Jason';person.age = 42;console.log(person.name,person.age);//'J

java8的新特性之一(Java Lambda表达式)

1:Java8的新特性 Lambda 表达式: 允许以更简洁的方式表示匿名函数(或称为闭包)。可以将Lambda表达式作为参数传递给方法或赋值给函数式接口类型的变量。 Stream API: 提供了一种处理集合数据的流式处理方式,支持函数式编程风格。 允许以声明性方式处理数据集合(如List、Set等)。提供了一系列操作,如map、filter、reduce等,以支持复杂的查询和转

Java面试八股之怎么通过Java程序判断JVM是32位还是64位

怎么通过Java程序判断JVM是32位还是64位 可以通过Java程序内部检查系统属性来判断当前运行的JVM是32位还是64位。以下是一个简单的方法: public class JvmBitCheck {public static void main(String[] args) {String arch = System.getProperty("os.arch");String dataM

详细分析Springmvc中的@ModelAttribute基本知识(附Demo)

目录 前言1. 注解用法1.1 方法参数1.2 方法1.3 类 2. 注解场景2.1 表单参数2.2 AJAX请求2.3 文件上传 3. 实战4. 总结 前言 将请求参数绑定到模型对象上,或者在请求处理之前添加模型属性 可以在方法参数、方法或者类上使用 一般适用这几种场景: 表单处理:通过 @ModelAttribute 将表单数据绑定到模型对象上预处理逻辑:在请求处理之前

eclipse运行springboot项目,找不到主类

解决办法尝试了很多种,下载sts压缩包行不通。最后解决办法如图: help--->Eclipse Marketplace--->Popular--->找到Spring Tools 3---->Installed。

JAVA读取MongoDB中的二进制图片并显示在页面上

1:Jsp页面: <td><img src="${ctx}/mongoImg/show"></td> 2:xml配置: <?xml version="1.0" encoding="UTF-8"?><beans xmlns="http://www.springframework.org/schema/beans"xmlns:xsi="http://www.w3.org/2001

Java面试题:通过实例说明内连接、左外连接和右外连接的区别

在 SQL 中,连接(JOIN)用于在多个表之间组合行。最常用的连接类型是内连接(INNER JOIN)、左外连接(LEFT OUTER JOIN)和右外连接(RIGHT OUTER JOIN)。它们的主要区别在于它们如何处理表之间的匹配和不匹配行。下面是每种连接的详细说明和示例。 表示例 假设有两个表:Customers 和 Orders。 Customers CustomerIDCus

22.手绘Spring DI运行时序图

1.依赖注入发生的时间 当Spring loC容器完成了 Bean定义资源的定位、载入和解析注册以后,loC容器中已经管理类Bean 定义的相关数据,但是此时loC容器还没有对所管理的Bean进行依赖注入,依赖注入在以下两种情况 发生: 、用户第一次调用getBean()方法时,loC容器触发依赖注入。 、当用户在配置文件中将<bean>元素配置了 lazy-init二false属性,即让