JavaScript 对象基础总结

一. 对象基本操作

1. 对象基础

一个对象由许多成员组成,每一个成员都有一个名字和一个值。每一个名字/值对被逗号(,)分隔开,并且名字和值之间由冒号(:)分隔。

2. 创建对象

2.1 创建对象常见的方式有以下六种:
  • new 操作符 + Object 创建对象
var person = new Object();
  • 字面式创建对象
var person ={
      name: "sidashen"
}
  • 工厂模式
function createPerson(name,age,family) {
    var o = new Object();
    o.name = name;
    o.age = age;
    o.family = family;
    o.say = function(){
        alert(this.name);
    }
    return o;
}

var person1 =  createPerson("sidashen",21,["lida","lier","wangwu"]);  
var person2 =  createPerson("shaonianyingxiong",18,["lida","lier","lisi"]);
  • 构造函数模式
function Person(name,age,family) {
    this.name = name;
    this.age = age;
    this.family = family;
    this.say = function(){
        alert(this.name);
    }
}
var person1 = new Person("lisi",21,["lida","lier","wangwu"]);
var person2 = new Person("lisi",21,["lida","lier","lisi"]);
  • 原型模式
function Person() {
  }
 
Person.prototype.name = "lisi";
Person.prototype.age = 21;
Person.prototype.family = ["lida","lier","wangwu"];
Person.prototype.say = function(){
   alert(this.name);
};

console.log(Person.prototype);  

var person1 = new Person(); 
console.log(person1.name); 
  • 混合模式(构造函数模式+原型模式)
function Person(name,age,family){
    this.name = name;
    this.age = age;
    this.family = family;
}

Person.prototype = {
    constructor: Person,
    say: function(){
          alert(this.name);
    }
}

var person1 = new Person("lisi",21,["lida","lier","wangwu"]);
      console.log(person1);
var person2 = new Person("wangwu",21,["lida","lier","lisi"]);
      console.log(person2);
2.2 创建一个对象

var obj = {};

3. 向对象中添加属性

语法:对象.属性名 = 属性值;

3.1 向obj中添加name属性
obj.name = "sidashen";
console.log(obj); // object { name = "sidashen"}
3.2 向obj中添加gender属性
obj.gender = "female";
console.log(obj); // object { gender = "female"}

4. 读取对象中的属性

语法:对象.属性名

obj.name; // "sidashen"

注意:如果读取对象中没有的属性不会报错,而是返回"undefined".

5. 修改属性值

语法:对象.属性名 = 新值

obj.name = "shaonianyingxiong";
console.log(obj); // object { name = "shaonianyingxiong"}

5. 删除对象的属性

语法:delete 对象.属性名

delete obj.name; //删除name属性
console.log(obj.name); // "undefined"

二. 属性名和属性值

1. 属性名

1.1 对象的属性名不强制要求遵守标识符规范。

如:obj.var = "hello"; 也可以命名,但不推荐。

1.2 如果使用特殊属性名,如@#$afj.,不能采用点表示法。
1.3 括号表示法。

语法:对象["属性名"] = 属性值

obj["123"] = 789; 
console.log(obj["123"]); // 读取时也需要采用这种表示法

括号表示法更灵活,在[]中可以直接传递一个变量,这样变量值是多少就读取那个属性。

var n = "123";
console.log(obj[n]); // "789"
var n = "nihao";
console.log(obj[n]); // "nihao"

2. 属性值

JavaScript对象的属性值,可以是任意的数据类型,如字符串,数组,函数,甚至可以是一个对象。

var obj2 = new Object();
obj2.name = "周杰伦";
// 将obj2设置为obj的属性
obj.test = obj2; // test的属性值为obj2
console.log(obj.test); // name = "周杰伦"
console.log(obj.test.name); // "周杰伦"

3. in运算符

通过该运算符,可检查一个对象中是否含有指定属性,有则返回true,没有返回false
语法:"属性名" in 对象
检查obj中是否有test2的属性

console.log("test2" in obj); // false
console.log("test" in obj); // true

三. 基本数据类型和引用数据类型

1.

基本数据类型:String, Number, Boolean, Null, Undefined;
引用数据类型:Object.

1.1 基本数据类型举例
var a = 123;
var b = a;
a++;
console.log(a); //124
console.log(b); //123

在这里会发现,ab相互独立,a的改变并不会影响b的属性。

1.2 引用数据类型举例
var obj = new Object();
obj.name = "周杰伦";
console.log(obj.name); // "周杰伦"
var obj2 = obj;
console.log(obj2.name); // "周杰伦"
//修改obj的name属性
obj.name = "Jay";
console.log(obj2.name); // "Jay"

在这里会发现,当改变obj的属性值时,obj2的属性值也跟着发生改变,这是为什么呢?

2. 基本数据类型和引用数据类型的区别

2.1 基本数据类型的特点
  • JavaScript中的变量都是保存到栈内存的;
  • 基本数据类型的值直接在栈内存中存储;
  • 值与值之间相互独立,修改一个变量不会影响其他变量。
2.2 引用数据类型的特点
  • 引用数据类型(对象)是保存在堆内存中的,每创建一个新对象,就会在堆内存中开辟一个新空间,而变量保存的是对象的内存地址(对象的引用)。如果两个变量保存的是同一个对象引用,当其中一个通过一个变量修改属性时,另一个也会受到影响。
  • 如果设置obj2的属性值为null:
obj2 = null;
console.log(obj); // object
console.log(obj2); // null

这里不会像之前一样同时变化是因为,当obj2的属性值为null,相当于将obj2里保存的内存地址与堆内存中对应的空间断开连接,所以obj不会被影响。

2.3 基本数据类型和引用数据类型在比较时的不同
  • 基本数据类型
var c = 10;
var d = 10;
console.log(c == d); // "ture"
  • 引用数据类型
var obj3 = new Object();
var obj4 = new Object();
obj3.name = "Chou";
obj4.name = "Chou";
console.log(obj3 == obj4); // "false"

当比较两个基本数据类型的值时,就是比较值,如 1 ===1;
而比较两个引用数据类型时,比较的是对象的内存地址,如果两个对象一模一样,但是地址不同,结果也返回false。所以在上面例子中,obj3obj4在堆内存中分别开辟了新的内存空间,有不一样的内存地址,所以比较相等的结果为false

四. 对象字面量

1. 使用对象字面量来创建一个对象

var obj = {};
console.log(obj); // "object"
obj.name = "周杰伦";
console.log(obj.name); // "周杰伦"

2. 使用对象字面量,可以在创建对象时,直接指定对象中的属性

语法:{属性名:属性值1,属性值2,...}

var obj2 = {name: "Jay"}
var obj2 = {name: "Jay", age: 40}
console.log(obj2); // name = "Jay", age = "40"

上述代码可简写同时也是常用写法为:

var obj2 = {
      name: "Jay", 
      age: 40
}
console.log(obj2); // name = "Jay", age = "40"

3. 对象字面量的属性名建议不加引号

4. 属性名和属性值是一组一组的名值对结构,名和值之间使用冒号(:)连接,多个名值对之间使用逗号(,)隔开。如果一个属性之后没有其他属性了,则不写逗号(,)。

五. this关键词

JavaScript函数中的this指向并不是在函数定义的时候确定的,而是在调用的时候确定的。换句话说,函数的调用方式决定了this指向。

JavaScript中,普通的函数调用方式有三种:直接调用、方法调用和new调用。除此之外,还有一些特殊的调用方式,比如通过bind()将函数绑定到对象之后再进行调用、通过call()apply()进行调用等。而ES6引入了箭头函数之后,箭头函数调用时,其this指向又有所不同

1. 直接调用

直接调用,就是通过函数名(...)这种方式调用。这时候,函数内部的this指向全局对象,在浏览器中全局对象是window,在Node中全局对象是global

// 简单兼容浏览器和 NodeJs 的全局对象
const _global=typeof window==="undefined"?global:window;
function test() {
    console.log(this===_global); // true
}
test();    // 直接调用

这里需要注意的一点是,直接调用并不是指在全局作用域下进行调用,在任何作用域下,直接通过 函数名(...) 来对函数进行调用的方式,都称为直接调用。

(function(_global) {
    // 通过 IIFE 限定作用域
    function test() {
        console.log(this === _global);  // true
    }
    test();     // 非全局作用域下的直接调用
})(typeof window === "undefined" ? global : window);

2. bind()对直接调用的影响

Function.prototype.bind()的作用是将当前函数与指定的对象绑定,并返回一个新函数,这个新函数无论以什么样的方式调用,其this始终指向绑定的对象。

const obj = {};
function test() {
    console.log(this === obj);
}
const testObj = test.bind(obj);
test(); // false
testObj();  // true

那么bind()干了什么?

const obj = {};
function test() {
    console.log(this === obj);
}
// 自定义的函数,模拟bind()对this的影响
function myBind(func, target) {
    return function() {
        return func.apply(target, arguments);
    };
}
const testObj = myBind(test, obj);
test();     // false
testObj();  // true

从上面的示例可以看到,首先,通过闭包,保持了target,即绑定的对象;然后在调用函数的时候,对原函数使用了apply方法来指定函数的this。当然原生的bind()实现可能会不同,而且更高效。但这个示例说明了bind()的可行性。

3. call和apply对this的影响

Function.prototype.apply()Function.prototype.call()这两方法的第一个参数都是指定函数运行时其中的this指向。

不过使用applycall的时候仍然需要注意,如果目录函数本身是一个绑定了this对象的函数,那applycall不会像预期那样执行。

const obj = {};
function test() {
    console.log(this === obj);
}
// 绑定到一个新对象,而不是 obj
const testObj = test.bind({});
test.apply(obj);    // true
// 期望 this 是 obj,即输出 true
// 但是因为testObj绑定了不是obj的对象,所以会输出false
testObj.apply(obj); // false

4. 方法调用

方法调用是指通过对象来调用其方法函数,它是对象.方法函数(...) 这样的调用形式。这种情况下,函数中的this指向调用该方法的对象。但是,同样需要注意bind()的影响。

const obj = {
    // 第一种方式,定义对象的时候定义其方法
    test() {
        console.log(this === obj);
    }
};
// 第二种方式,对象定义好之后为其附加一个方法(函数表达式)
obj.test2 = function() {
    console.log(this === obj);
};
// 第三种方式和第二种方式原理相同
// 是对象定义好之后为其附加一个方法(函数定义)
function t() {
    console.log(this === obj);
}
obj.test3 = t;
// 这也是为对象附加一个方法函数
// 但是这个函数绑定了一个不是 obj 的其它对象
obj.test4 = (function() {
    console.log(this === obj);
}).bind({});
obj.test();     // true
obj.test2();    // true
obj.test3();    // true
// 受 bind() 影响,test4 中的 this 指向不是 obj
obj.test4();    // false

5. 方法中this指向全局对象的情况

这里说的是方法中而不是方法调用中。方法中的this指向全局对象,如果不是因为bind(),那就一定是因为不是用的方法调用方式。

const obj = {
    test() {
        console.log(this === obj);
    }
};
const t = obj.test;
t();    // false

t就是objtest方法,但是t()调用时,其中的this指向了全局。

6. new调用

在ES6之前,每一个函数都可以当作是构造函数,通过new调用来产生新的对象(函数内无特定返回值的情况下)。而ES6改变了这种状态,虽然class定义的类用typeof运算符得到的仍然是"function",但它不能像普通函数一样直接调用;同时,class中定义的方法函数,也不能当作构造函数用new来调用。
而在ES5中,用new调用一个构造函数,会创建一个新对象,而其中的this就指向这个新对象。这没有什么悬念,因为new本身就是设计来创建新对象的。

var data = "Hi";    // 全局变量
function AClass(data) {
    this.data = data;
}
var a = new AClass("Hello World");
console.log(a.data);    // Hello World
console.log(data);      // Hi
var b = new AClass("Hello World");
console.log(a === b);   // false

7. 箭头函数中的this

箭头函数没有自己的this绑定。箭头函数中使用的this,其实是直接包含它的那个函数或函数表达式中的this

const obj = {
    test() {
        const arrow = () => {
            // 这里的 this 是 test() 中的 this,
            // 由 test() 的调用方式决定
            console.log(this === obj);
        };
        arrow();
    },
    getArrow() {
        return () => {
            // 这里的 this 是 getArrow() 中的 this,
            // 由 getArrow() 的调用方式决定
            console.log(this === obj);
        };
    }
};
obj.test();     // true
const arrow = obj.getArrow();
arrow();        // true

示例中的两个this都是由箭头函数的直接外层函数(方法)决定的,而方法函数中的this是由其调用方式决定的。上例的调用方式都是方法调用,所以this都指向方法调用的对象,即obj
箭头函数让大家在使用闭包的时候不需要太纠结this,不需要通过像_this这样的局部变量来临时引用this给闭包函数使用。来看一段Babel对箭头函数的转译可能能加深理解。

// ES6
const obj = {
    getArrow() {
        return () => {
            console.log(this === obj);
        };
    }
}    
// ES5,由 Babel 转译
var obj = {
    getArrow: function getArrow() {
        var _this = this;
        return function () {
            console.log(_this === obj);
        };
    }
};

另外需要注意的是,箭头函数不能用new调用,不能bind()到某个对象(虽然bind()方法调用没问题,但是不会产生预期效果)。不管在什么情况下使用箭头函数,它本身是没有绑定this的,它用的是直接外层函数(即包含它的最近的一层函数或函数表达式)绑定的this

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,816评论 6 492
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,729评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 158,300评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,780评论 1 285
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,890评论 6 385
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,084评论 1 291
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,151评论 3 410
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,912评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,355评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,666评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,809评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,504评论 4 334
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,150评论 3 317
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,882评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,121评论 1 267
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,628评论 2 362
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,724评论 2 351

推荐阅读更多精彩内容

  • 函数和对象 1、函数 1.1 函数概述 函数对于任何一门语言来说都是核心的概念。通过函数可以封装任意多条语句,而且...
    道无虚阅读 4,550评论 0 5
  • 第一章 错误处理: 错误: 程序运行过程中,导致程序无法正常执行的现象(即bug) 现象: 程序一旦出错,默认会报...
    fastwe阅读 1,101评论 0 1
  • 第3章 基本概念 3.1 语法 3.2 关键字和保留字 3.3 变量 3.4 数据类型 5种简单数据类型:Unde...
    RickCole阅读 5,108评论 0 21
  • Swift1> Swift和OC的区别1.1> Swift没有地址/指针的概念1.2> 泛型1.3> 类型严谨 对...
    cosWriter阅读 11,093评论 1 32
  • 概要 64学时 3.5学分 章节安排 电子商务网站概况 HTML5+CSS3 JavaScript Node 电子...
    阿啊阿吖丁阅读 9,146评论 0 3