第1关:对象的创建

任务描述

本关任务:创建你的第一个JavaScript对象。

相关知识

JavaScript是一种基于对象(Object-based)的语言,在JavaScript中,对象的创建和Java不同,既有Java使用的构造函数方式,也有其他方法。

对象的定义

JavaScript中的一切都是对象,这是该语言的一个很大的特点。像字符串、数组等已经定义的对象叫做内置对象。用户自己也可以定义对象,叫做自定义对象。本实训讲的对象特指自定义对象,自定义对象指数据和函数(又叫方法)的集合。数据指变量名和变量的值构成的组合。如下图所示:

下面介绍五种创建对象的方法,其中通过对象字面量和使用构造函数创建对象最常用。

对象字面量

这是最常用的创建对象的方法,通过新建一个键值对的集合(对象字面量)创建对象,如下:

 
  1. var song = {
  2. name:"Liekkas",
  3. time:180,
  4. "song language":English,
  5. singer: {
  6. singerName:"Sofia Jannok",
  7. singerAge:30
  8. }
  9. };

键值对中的键指的是属性的名字,若其中含有空格,名字需要用双引号包含在内。值指的是属性的值,可以是基本类型:如字符串,数字,布尔型,也可以是一个对象。键值对之间用逗号隔开,最后一个键值对后面没有逗号,所有的键值对在一个大括号中。

通过关键字new创建对象

通过new关键字创建对象也是一个常用的方法。如下:

 
  1. var Store = new Object();//创建对象的一个实例
  2. Store.name = "lofo Market";
  3. Store.location = "NO.13 Five Avenue";
  4. Store.salesVolume = 1000000;

通过上面的代码,我们就能创建一个名为Store的对象。

通过工厂方法创建对象

工厂方法就是通过函数创建对象,函数封装了创建对象的过程。

这是一种通过函数创建对象的方法,函数封装了对象的创建过程,创建新对象时只需要调用该函数即可。这种方法适合于一次创建多个对象。

 
  1. //对象的创建函数
  2. function createStoreObject(name,location,salesVolume) {
  3. var store = new Object();
  4. store.name = name;
  5. store.locaion = location;
  6. store.salesVolume = salesVolume;
  7. store.display = function() {
  8. console.log(this.name);
  9. };
  10. return store;
  11. }
  12. //利用该函数创建一个对象
  13. var store1 = createStoreObject("panda express","No.1,People Street",200000);

这样就创建了一个名为store1的对象,注意这个对象除了属性之外还有一个方法display。要创建更多的类似store1的对象,直接调用该函数即可。

使用构造函数创建对象

上面虽然也是通过函数创建对象,但不是构造函数,只是普通函数。构造函数名必须以大写字母开头,函数体内没有返回语句。

 
  1. //构造函数
  2. function Store(name,location,salesVolume) {
  3. this.name = name;
  4. this.locaion = location;
  5. this.salesVolume = salesVolume;
  6. }
  7. //创建对象的实例
  8. var myStore = new Store("KeyExp","No.1,L.Street",540000);

上面的代码首先是Store对象的构造函数,然后用该构造函数创建了Store对象的一个实例myStore

使用原型(prototype)创建对象(不懂)

当我们创建一个函数时,函数就会自动拥有一个prototype属性,这个属性的值是一个对象,这个对象被称为该函数的原型对象。也可以叫做原型。

当用new关键字加函数的模式创建一个对象时,这个对象就会有一个默认的不可见的属性[[Prototype]],该属性的值就是上面提到的原型对象。如下图所示:

JavaScript中每个对象都有一个属性[[Prototype]],指向它的原型对象,该原型对象又具有一个自己的[[Prototype]],层层向上直到一个对象的原型为null。根据定义,null 没有原型,并作为这个原型链中的最后一个环节。如下图所示:

这种方法是对使用构造函数创建对象的改进,使用构造函数创建一个对象时,会把构造函数中的方法(上面的构造函数只有属性的键值对,没有方法)都创建一遍,浪费内存,使用原型不存在这个问题。

 
  1. function Store() {};
  2. Store.prototype.name = "SF Express";
  3. Store.prototype.locaion = "Hong Kong";
  4. Store.prototype.salesVolume = 1200000000;
  5. //创建对象
  6. var myStore = new Store();
  7. //创建一个新的对象
  8. var hisStore = new Store();
  9. hisStore.name = "STO Express";//覆盖了原来的name属性

这种方法的好处是,创建一个新的对象时,可以更改部分属性的值。

编程要求

本关的编程任务是补全右侧代码片段中beginend中间的代码,具体要求如下:

  • 使用对象字面量方法创建名为student的对象,有两个属性namegender,他们的值分别是mainJs()函数的参数a和参数b

  • 使用已给的构造函数Car(plate,owner)创建一个对象myCar,它的两个属性的值分别是参数c和参数d

  • 使用原型创建一个对象myJob,它的构造函数是Job(company,salary),它的两个属性的值已经被设置,你需要用参数e覆盖属性company的值;

  • 具体请参见后续测试样例。

本关涉及的代码文件CreateObject.js的代码框架如下:

 
  1. function Car(plate,owner) {
  2. this.plate = plate;
  3. this.owner = owner;
  4. }
  5. function Job() {};
  6. Job.prototype.company = "myCompany";
  7. Job.prototype.salary = 12000;
  8. function mainJs(a,b,c,d,e) {
  9. //请在此处编写代码
  10. /********** Begin **********/
  11. /********** End **********/
  12. return student.name+student.gender+myCar.plate+myCar.owner+myJob.company;
  13. }

测试说明

测试过程:

  • 平台将读取用户补全后的CreateObject.js

  • 调用其中的mainJs()方法,并输入若干组测试数据;

  • 接着根据测试的输出判断程序是否正确。

以下是测试样例:

测试输入: Alice,girl,JA12345,Zhang,ST Technology 预期输出: AlicegirlJA12345ZhangST Technology

function Car(plate,owner) {
    this.plate = plate;
    this.owner = owner;
}

function Job() {};
Job.prototype.company = "myCompany";
Job.prototype.salary = 12000;

function mainJs(a,b,c,d,e) {
	//请在此处编写代码
    /*********bigin*********/

var student= {
    name:a,
    gender:b
};

var myCar=new Car(c,d);

var myJob=new Job;
myJob.company=e;
    /*********end*********/
    return student.name+student.gender+myCar.plate+myCar.owner+myJob.company;
}

第2关:属性的增删改查

任务描述

Luma Restaurant以前的财务人员在统计销售额的时候不小心把数据弄错了,现在的财务人员想通过一个JavaScript函数方便的修改数据,并署上自己的名字,请你帮助她完成这个任务吧!

本关任务:根据本小结内容,完成JavaScript对象属性值的获取和修改。

相关知识

Java中,当实体类建立以后,类的属性只能获取与修改,不能增加与删除。但是因为JavaScript是动态类型的语言,JavaScript中对象的属性具有增删改查所有的操作。

属性的获取

方式一

属性的获取有两种方式,一种是使用.符号,符号左侧是对象的名字,符号右侧是属性的名字,如下:

 
  1. var student = {name:"Alice",gender:"girl"};
  2. console.log(student.name);//输出Alice

这种情况下属性名必须是静态的字符串,即不能是通过计算或者字符串的拼接形成的字符串。

方式二

另外一种是使用[""]符号,符号的左边是对象的名字,双引号中间是属性的名字,这种情况下属性名可以是一个表达式,只要表达式的值是一个字符串即可。如下:

 
  1. var student = {name:"Alice",gender:"girl"};
  2. console.log(student["name"]);//输出Alice

有两种情况必须使用第二种方式:

  • 属性名含有空格字符,如student["first name"],这时不能用student.first name代替,编译器无法解释后者;

  • 属性名动态生成,比如用for循环获取前端连续id的值,这种id名之间一般有特定关系。如下面的例子:

 
  1. for(int i = 0;i < 5;i ++) {
  2. console.log(student["id"+i]);
  3. }

属性的修改与新增

属性的修改指修改已有属性的值,这个直接用赋值符号即可。

属性的新增与修改在形式上完全相同,区别仅在于编译器会根据属性的名字判断是否有该属性,有则修改,没有则新增。

 
  1. var student = {
  2. name:"Kim",
  3. age:21
  4. };
  5. student.age = 20;//修改属性,覆盖了原来的值21
  6. student.gender = "female";//新增属性gender

删除属性

JavaScript中的属性还可以删除,这在其他的面向对象语言如Java或者C++中是无法想象的,删除通过delete运算符实现。删除成功返回布尔型true,删除失败也是返回true,所以在删除之前需要判断一个属性是否存在,这个内容将在下一关讲解。

需要注意的是,对象只能删除自己特有的属性,而不能删除继承自原型对象的属性。同时,对象在删除属性时,要防止删除被其他对象继承的属性,因为这样会导致程序出错。

 
  1. var Store = new Object();
  2. Store.name = "lofo Market";
  3. Store.location = "NO.13 Five Avenue";
  4. console.log(delete Store.name);//删除成功,输出true
  5. console.log(Store.name);//已删除,返回undefined
  6. delete Store.prototype;//删除失败,非自有属性

编程要求

请补全右侧beginend之间的代码片段,使函数reviseAttribute(reviser,date,attvalue) 具备根据传入参数修改store对象的属性的功能,具体要求如下:

  • 如果调用函数reviseAttribute(reviser,date,attvalue)并传入值 Alice,1,1000那么对应storeday1属性的值就修改为1000,accountant属性的值修改为Alice
  • 具体请参见后续测试样例。

本关涉及的代码文件operateAttribute.js的代码框架如下:

 
  1. var store = {
  2. name:"Luma Restaurant",
  3. location:"No 22,Cot Road",
  4. accountant:"Vivian Xie",
  5. day1:3200,
  6. day2:3200,
  7. day3:3200,
  8. day4:3200,
  9. day5:3200,
  10. day6:3200,
  11. day7:3200,
  12. day8:3200,
  13. day9:3200,
  14. day10:3200
  15. }
  16. function reviseAttribute(date,attValue) {
  17. //Convert string to integer
  18. attValue = parseInt(attValue);
  19. //请在此处编写代码
  20. /********** Begin **********/
  21. /********** End **********/
  22. var totalSales = store["day1"]+store["day2"]+store["day3"]+store["day4"]+store["day5"]+store["day6"]+store["day7"]+store["day8"]+store["day9"]+store["day10"];
  23. return toalSales+store.accountant;
  24. }

测试说明

测试过程:

  • 平台将读取用户补全后的operateAttribute.js

  • 调用其中的mainJs()方法,并输入若干组测试数据;

  • 接着根据测试的输出判断程序是否正确。

以下是测试样例:

测试输入: Alice,2,1 预期输出: 28801Alice

测试输入: Kim,10,0 预期输出: 28800Kim

var store = {
	name:"Luma Restaurant",
	location:"No 22,Cot Road",
	accountant:"Vivian Xie",
	day1:3200,
	day2:3200,
	day3:3200,
	day4:3200,
	day5:3200,
	day6:3200,
	day7:3200,
	day8:3200,
	day9:3200,
	day10:3200
}
function reviseAttribute(reviser,date,attValue) {
    //Convert string to integer
    attValue = parseInt(attValue);
    //请在此处编写代码
    /*********begin*********/
store["day"+date]=attValue;
    store.accountant = reviser;
	/*********end*********/
    var totalSales =  store["day1"]+store["day2"]+store["day3"]+store["day4"]+store["day5"]+store["day6"]+store["day7"]+store["day8"]+store["day9"]+store["day10"];
    return totalSales+store.accountant;
}

任务描述

本关任务:给定一个属性的名字,请先判断它属于哪一个对象,然后返回该对象的所有自有属性名连接成的字符串。

如:school对象有三个自有属性name,location,studentNum,如果给定name,你需要返回字符串namelocationstudentNum

相关知识

JavaScript编程实践中,如果我们调用别人的接口,常常需要了解实体是否具有某个属性。

属性的检测

属性的检测指检查对象是否有某个属性或者方法,需要使用运算符inin的左侧是属性或者方法名,右侧是检查对象,对象有该属性或者方法则返回true,否则返回false,如下:

 
  1. var school = {
  2. name:"SJTU",
  3. location:"ShangHai",
  4. studentNum:40000,
  5. display:function() {
  6. console.log(this.name);
  7. }
  8. };
  9. //检测属性
  10. console.log("name" in school);//输出true
  11. console.log("sales" in school);//输出false
  12. //检测方法
  13. console.log("display" in school);//输出true
  14. console.log("print" in school);//输出false

这里的属性名是字符串,必须用双引号包含在内。

还可以用hasOwnProperty()检测对象是否具有某个自有属性或方法。括号内的参数是属性或者方法的名字。

所谓自有属性或者方法,是指对象自己定义的属性或者方法,而不是从原型链上继承来的。关于原型链,请参考本实训第一关。

 
  1. var school = {
  2. name:"SJTU",
  3. location:"ShangHai",
  4. studentNum:40000,
  5. display:function() {
  6. console.log(this.name);
  7. }
  8. };
  9. console.log(school.hasOwnProperty("studentNum"));//true
  10. console.log(school.hasOwnProperty("hasOwnProperty"));//false

因为hasOwnProperty方法继承自object对象,不是自有方法,所以返回false

属性的枚举

定义:属性的枚举指按顺序逐个的列出属性的名字。如下面的例子:

 
  1. var person = {
  2. name:"Ye",
  3. gender:"Gril",
  4. age:23,
  5. salary:23000,
  6. height:1.78
  7. }

根据前面的知识,我们知道对象person有五个属性,所谓枚举,就是依次列出这五个属性的名字,即:name、gender、age、salary、height,至于它们排列的顺序,在不同的浏览器中的结果不同,这里不讨论。

在继续下面的知识点之前,首先要知道一个概念:可枚举性(enumerable),这是对象的属性的一个性质,用户自己定义的属性默认为可枚举,系统内置的对象的属性默认为不可枚举。

枚举属性有三种方法:

  • for...in...循环; 可以枚举所有可枚举的属性,包括继承的属性。如下:
     
      
    1. //首先定义一个school对象,它从原型链上继承的属性都是不可枚举的,而下面自定义的四个属性或者方法都是可枚举的
    2. var school = {
    3. name:"SJTU",
    4. location:"ShangHai",
    5. studentNum:40000,
    6. display:function() {
    7. console.log(this.name);
    8. }
    9. };
    10. //枚举school的属性
    11. //下面的圆括号中的att表示对象的属性,school表示对象
    12. for(var att in school) {
    13. //依次输出name,location,studentNum,display
    14. console.log(att);
    15. }
    圆括号里面的表达式中,att表示对象的属性,school表示该对象,这个循环将依次遍历对象的所有可枚举属性,每次输出一个属性的值。
  • Object.getOwnPropertyNames(); 括号中有一个参数,是要枚举的对象。该表达式将返回对象的所有自有属性的名字,不区分是否可枚举,结果以字符串数组的形式呈现,如下:

     
      
    1. //定义一个school对象
    2. var school = {
    3. name:"SJTU",
    4. location:"ShangHai",
    5. studentNum:40000,
    6. display:function() {
    7. console.log(this.name);
    8. }
    9. };
    10. //为school对象增加一个不可枚举的属性range
    11. Object.defineProperty(school, "range", {
    12. value: 4,//设置range属性的值
    13. enumerable: false//设置range属性为不可枚举
    14. });
    15. //输出["name","location","studentNum","display","range"]
    16. console.log(Object.getOwnPropertyNames(school));

    如果用上面的for...in...循环,range属性是不能够枚举到的。

  • Object.keys(); 括号中有一个参数,是要枚举的对象。该表达式返回可枚举的自有属性,以字符串数组的形式。所以这里对属性的要求更加严格,既要求是自有属性,又要求可枚举。

     
      
    1. var school = {
    2. name:"SJTU",
    3. location:"ShangHai",
    4. studentNum:40000,
    5. display:function() {
    6. console.log(this.name);
    7. }
    8. };
    9. //为school对象增加一个不可枚举的属性range
    10. Object.defineProperty(school, "range", {
    11. value: 4,//设置range属性的值
    12. enumerable: false//设置range属性为不可枚举
    13. });
    14. //输出["name","location","studentNum","display"]
    15. console.log(Object.keys(school));

总结一下上面三个方法对属性是否自有,是否可枚举的要求:

方法名是否要求可枚举是否要求自有
for...in...
Object.getOwnPropertyNames()
Object.keys()

编程要求

本关的编程任务是补全右侧代码片段中beginend中间的代码,具体要求如下:

  • 有两个可选的对象orangecar,判断给定的属性名a属于哪一个对象;

  • 返回该对象的所有自有属性名组成的字符串,例如:如果判断为car,则返回brandpricemodel

  • 给定的两个对象的自有属性都是可枚举的;

  • 具体请参见后续测试样例。

本关涉及的代码文件AttributeDetect.js的代码框架如下:

 
  1. var orange = {
  2. weight:"200g",
  3. color:"orange",
  4. taste:"sour"
  5. };
  6. var car = {
  7. brand:"Jaguar",
  8. price:"$80000",
  9. model:"XFL"
  10. }
  11. function mainJs(a) {
  12. //请在此处编写代码
  13. /********** Begin **********/
  14. /********** End **********/
  15. }

测试说明

测试过程:

  • 平台将读取用户补全后的AttributeDetect.js

  • 调用其中的mainJs()方法,并输入若干组测试数据;

  • 接着根据测试的输出判断程序是否正确。

以下是测试样例:

测试输入: price 预期输出: brandpricemodel

var orange = {
    weight:"200g",
    color:"orange",
    taste:"sour"
};
var car = {
    brand:"Jaguar",
    price:"$80000",
    model:"XFL"
}
function mainJs(a){
	//请在此处编写代码
    /*********begin*********/

var result =  "";
    if(a in car){
        for(att in car){
            result+=att;
        }
    }
    else{
        for(att in orange){
            result+=att;
        }
    }
    return result;
    /*********end*********/
}

Logo

开放原子开发者工作坊旨在鼓励更多人参与开源活动,与志同道合的开发者们相互交流开发经验、分享开发心得、获取前沿技术趋势。工作坊有多种形式的开发者活动,如meetup、训练营等,主打技术交流,干货满满,真诚地邀请各位开发者共同参与!

更多推荐