第八章 对象

1 对象的概念

广义的理解: 一切皆对象,数组、函数都是对象的一种。

狭义的理解: Object 数据类型,是对象类型中的一种,与Array、Function是等价的。

2 Object 类型

2.1 什么是Object

  • 对象(Object)是值的无序集合
  • 别称:字典、散列、关联数组、散列表。
  • 对象由属性组成, 如果属性的类型是 function,该属性称为方法。

2.2 声明一个Object类型的数据

1) 直接量方式 {}

2) 构造函数方式 new Object()

// 定义对象 // 直接量方式
// 指定属性名,属性名对应的值, 属性名
var data = {
  name:'李达康', 
  age:42, 
  numbers:[100, 200, 300, 400], 
  say: function(){
      console.log('我会说话');
    }, 
  eat: function(){
      console.log('我会吃');
    }
};
// 在对象外面给对象添加属性
data.sex = 'male';
data.sleep = function(){
  console.log('我会睡');
};


// 构造函数方式
// 创建对象
var obj = new Object();
// 给对象添加属性和方法
obj.name = '小芳芳';
obj.age = 19;
obj.eat = function(){
    console.log('我喜欢吃')
};

2.3 属性操作

① 属性的读取和设置(读写操作)

1). 运算符, 如obj.name

2)[] 运算符,如 obj['name']。 属性名要以字符串形式给出,也可以以变量的形式给出。

注意:

设置属性值的时候,属性已经存在,就修改属性值;如果属性不存在,就添加属性。 获取对象中不存在的属性的时候,返回 undefined

注意:在以下情况下我们必须使用[]语法操作:

① 如果对象的属性名不符合标识命名规范(变量名的命名规范)。

② 如果需要使用变量的值作为属性名。

// 指定属性名,属性名对应的值, 属性名
var data = {
  name:'高育良', 
  age:58, 
  numbers:[100, 200, 300, 400], 
  say: function(){
          console.log('我会说话');
    }, 
  eat: function(){
      console.log('我会吃');
    }
};

// 修改和添加属性
data.name = '大艳艳';   // 使用 . 运算符
data.sex = 'female';
data['numbers'] = [1,2,3,4];  // 使用 [] 运算符

// 读取属性 .
console.log(data.name, data.age);
data.eat();
// 读取属性 可以使用 [], 属性名必须以字符串的形式给出。
console.log(data['age']);
data['say']();

//获取对象中不存在的属性,得到 undefined
console.log(data.length);  // undefined

② 属性的遍历

使用 for ... in 循环可以遍历对象中的属性。

for (var i in data) {
  // i  是属性名
    // obj[i];  是属性值,只能使用[]形式获取属性值。
  console.log(i, data[i]);  
}

③ 删除对象中的某个属性

使用 delete 运算符

delete obj.name;
ddlete obj['name'];

④ 判断对象中是否存在某个属性

判断对象中是否存在某个属性,使用 in 运算符,是个二元运算符,表达式的结果是布尔值。

'name' in obj;  //属性名以字符串的形式给出,表达式的结果是布尔值。

3 构造函数

3.1 什么是构造函数

1)对象是一个实际的存在, 构造函数是对对象的描述。

2)对象是构造函数的实例,构造函数是对象的抽象。

3)JS 中的构造函数相当于其他编程语言的(ES6 中也引入了类的概念)。

4)每一对象都有与之对应的构造函数。

5)一个构造函数可以对应很多对象, 一个对象只有一个构造函数。

3.2 判断对象的构造函数

1)instanceof 运算符,语法:对象 intanceof 构造函数。 返回布尔值。

2).constructor 所有的对象都有该属性,返回对象的构造函数

var obj1 = {name:'沙瑞金'};
var obj2 = new Object();
var list = [];
var msg = 'hello';
var num = 1001;
var box = document.getElementById('box');

console.log(obj1.constructor);  // ƒ Object() { [native code] }
console.log(obj2.constructor);  // ƒ Object() { [native code] }
console.log(list.constructor);  // ƒ Array() { [native code] }
console.log(msg.constructor);   // ƒ String() { [native code] }
console.log(num.constructor);   // ƒ Number() { [native code] }
console.log(true.constructor);  // ƒ Boolean() { [native code] }
console.log(box.constructor);   // ƒ HTMLDivElement() { [native code] }
console.log(alert.constructor); // ƒ Function() { [native code] }

console.log(100 instanceof Number); //false  数字是原始类型
console.log(new Number(1000) instanceof Number); //true
console.log([] instanceof Array); //true
console.log(alert instanceof Function);  //true

3.3 自定义构造函数

定义一个构造函数与定义一个普通函数是一样的,我们通常会把构造函数的名字首字母大写,以示区别(这只是人为区分不是语法)。

// 自定义构造函数 与定义普通函数一个样
//定义描述人的 构造函数
function Person() {
  //描述一下实例所需要的属性
  this.name = '小莉莉';
  this.age = 18;
}

// 根据构造函数 实例化对象
var p1 = new Person();
var p2 = new Person();

console.log(typeof p1);  // object
console.log(typeof p2);  // object
console.log(p1.constructor); // function Person() ...
console.log(p2.constructor); // function Person() ...

3.4 对象的实例化

使用new 运算符可以把构造函数实例化成一个对象,不论构造函数是系统内置的还是我们自定义的。

每实例化一个对象,内存中就会开辟一块空间来存储该对象。

//自定义构造函数
function Person() {
}

// 实例化自定义的构造函数
var p = new Person();

// 实例化内置的构造函数
var arr = new Array();
var obj = new Object();
var fn = new Function();

3.5 构造函数和普通函数

任何的函数都可以是普通函数,也可以是构造函数,就看我们怎么去使用。

//创建一个类(函数)
function Car(name,color,price){
   this.name = name;
   this.color = color;
   this.price = price;
   this.run = function(){
      console.log('跑的很快~');
   }
}

//普通函数调用
var result = Car('奔驰','black',200000);
console.log(result);

//构造函数调用            
var c1 = new Car('劳斯莱斯','red',10000000);
console.log(c1);
c1.run();

4 this

4.1 this 的概念

1) this 是 JS 内置的一个变量,本质上是一个对象

2)通常在函数或方法当中使用,代表这个函数的调用者。

4.2 this 的指向

this 的指向分为以下两种情况:

1)在构造函数使用 this, this 指向实例化之后的对象。

2)在方法(所谓函数本质上其实是 window 对象的方法)中使用this,this 指向调用该方法的对象。

注意:

在函数中使用 this,this 指向 window 对象。函数本质上其实是 window 对象的方法,所以在函数中使用 this 与第二种情况本质是一样的。

// 在方法中使用this
var obj = {};
obj.name = '曹操';
obj.age = 29;
obj.say = function(){
  console.log('我叫'+this.name+',我今年'+this.age+'岁了');
};
//调用对象的方法
obj.say();   // say 里面的 this 指向 obj。


// 自定义函数(构造函数)
function Dog(name){
  this.name = name;
  console.log(this);
}

//实例化,这么使用 Dog 便是构造函数,里面的 this 指向要实例化成的对象
var d = new Dog('旺财');  

// 调用函数,此时调用改函数的是 window,所以 this 指向window。
Dog('小黄');

4.3 window 对象

1)window 是浏览器端 JS 的全局对象。

2)打开浏览器,window 对象就会自动生成。

3)所有的全局属性(全局变量和函数)都是 window 对象的属性。

4)使用 window 的属性和方法的时候,通常可以省略window。

5)系统的函数 alert、prompt、Numbe、Boolean、Array、Object等 也是window的属性。

5 原型

5.1 原型的概念

1)原型是个对象。

2)所有的对象都有原型。

3)对象可以继承原型的属性(包括方法)。

5.2 获取原型对象

隐式原型: 对象.__proto__

显示原型: 构造函数.prototype

一个对象的 __proto__属性与其构造函数的prototype属性指向同一个对象(该对象的原型对象)

// 定义一个数组对象
var arr = [10,20,30];

// 使用对象直接获取 隐式原型
arr.__proto__
// 通过构造函数来获取原型 显示原型
Array.prototype; 
// 两种获取方式获取的原型是一样的
arr.__proto__ === Array.prototype


// 自定义构造函数
function Person(){
  this.username = '小艳艳';
}
var p = new Person();
console.log(p.__proto__);
console.log(Person.prototype);
console.log(p.__proto__ === Person.prototype); // true

proto和__proto__的区别

5.4 对象、构造函数、原型之间关系

1)对象和构造函数: 对象是构造函数的实例, 对象由构造函数产生。

2)对象和原型: 每个对象都有原型,可以继承原型上的属性。

3)构造函数和原型: 如果对象的构造函数相同,对象的原型也相同。

5.4 原型的应用

我们在写构造函数的时候,通常会把方法定义在它的原型上,这样可以让构造函数所有的实例化对象资源共享,节省内存。

// 自定义构造函数
function Person(name, age) {
  //设置对象的属性
  this.name = name;
  this.age = age;
}
// 给原型添加方法
// 把方法定义到原型上,可以节省内存
Person.prototype.say = function(){
  console.log('我是'+this.name+',我今年'+this.age+'岁了');
};


//实例化对象
// p1 和 p2 两个对象都有 say 方法,但 say 方法只存了一次。
var p1 = new Person('曹操', 18);
var p2 = new Person('小乔', 18);

5.5 判断属性是否属于对象自身

使用方法 hasOwnProperty, 每个对象都有该方法,参数是属性名, 返回布尔值。判断属性是否是对象自身的属性,自身的属性返回 true,不是自身的属性(原型上的属性或者对象自身和原型都没有的属性)返回 false。

// 自定义构造函数
function Person(name, age) {
  //设置对象的属性
  this.name = name;
  this.age = age;
}
// 给原型添加方法
// 把方法定义到原型上,可以节省内存
Person.prototype.say = function(){
  console.log(this); //谁调用了this,this就指向谁。
  console.log('我是'+this.name+',我今年'+this.age+'岁了');
};


//实例化对象
var p1 = new Person('曹操', 18);

console.log(p1.hasOwnProperty('name'));  // true 自身的属性
console.log(p1.hasOwnProperty('age'));   // true 自身的属性
console.log(p1.hasOwnProperty('say'));   // false 原型上的属性
console.log(p1.hasOwnProperty('constructor'));  // false 原型上的属性
console.log(p1.hasOwnProperty('grade'));  // false 自身和原型都没有的属性

5.6 创建对象并且指定原型

Object.create()方法创建一个新对象,使用现有的对象来提供新创建的对象的原型。

语法:

Object.create( proto[, propertiesObject])

参数

  • proto

    新创建对象的原型对象。如果该参数是 null,那创建的对象就是一个没有原型的对象。

  • propertiesObject

    可选。如果没有指定为 undefined。则是要添加到新创建对象的不可枚举(默认)属性(即其自身定义的属性,而不是其原型链上的枚举属性)对象的属性描述符以及相应的属性名称。

返回值:

一个新对象,带着指定的原型对象和属性。

var person = {
  isHuman: false,
  printIntroduction: function() {
    console.log(`My name is ${this.name}. Am I human? ${this.isHuman}`);
  }
};

var me = Object.create(person);

me.name = 'Matthew'; // "name" is a property set on "me", but not on "person"
me.isHuman = true; // inherited properties can be overwritten

me.printIntroduction(); // expected output: "My name is Matthew. Am I human? true"

6 原型链

6.1 什么是原型链

任何对象都有原型对象,原型还是一个对象,既然是一个对象就会有自己的原型,那原型的原型仍然还有原型,可以依次向上找原型,直到找到一个没有原型的对象。这样就形成了一条原型链。

原型链

6.2 属性查找过程

原型链描述的就是是对象查找属性或者方法的过程。

属性的查找会遵循如下过程:

1)对象在查找找属性的时候,先从自身去找看有没有这个属性,如果有,直接使用这个属性的值。

2)如果没有,会沿着原型链向上找,如果找到就使用这个属性的值且停止查找,如果没找到继续向上找直到原型链的终点。

3)如果找到原型链的终点还没有找到,就返回undefined(代表已经找到顶了)

6.3 原型链图例

原型链

7 值类型和引用类型

7.1 值类型和引用类型的概念

原始类型,也叫值类型,还可以被称为不可变类型

对象类型,也叫引用类型,还可被称为可变类型

// 值类型
var a = 100;
var b = a;
a = 200;
console.log(b); //100
console.log('');


// 引用类型
var obj1 = {age: 100};
var obj2 = obj1;  //由于是引用类型,obj1给obj2是地址,导致obj1和obj2指向一个对象
obj1.age = 200;
console.log(obj2.age); //200
console.log('');


// 对变量重新赋值
// 起始是把obj2指向了新的对象, obj2和obj1再无关系了。
obj2 = {}; // 给obj2重新赋值了一个对象 (和修改obj2的属性不一样)

7.2 值类型和引用类型的内存存储方式

值类型的特点:占用空间固定,变量名和变量都保存在栈内存中。

值类型

引用类型的特点: 变量的值存储在堆内存中,而栈内存中存储的是变量名和值的地址。

引用类型

7.3 值类型和引用类型经典面试题

//1、
var num1 = 10;
var num2 = num1;
num1 = 20;
console.log(num1);
console.log(num2);


//2、
var num = 50;
function f1(num) {
    num = 60;
    console.log(num);
}
f1(num);
console.log(num);


//3、
var num1 = 55;
var num2 = 66;
function f1(num, num1) {
    num = 100;
    num1 = 100;
    num2 = 100;
    console.log(num);
    console.log(num1);
    console.log(num2);
}
f1(num1, num2);
console.log(num1);
console.log(num2);
console.log(num);


// 4、
// 函数传参如果传的是基本数据类型和传引用(对象)有什么区别
//4-1、
var a = 10;
var b = 20;
function add(a,b){
    a = 30;
    return a + b;
}
add(a,b);
console.log(a);
//4-2、
function f1(arr){
    for(var i = 0; i < arr.length; i++){
        arr[i] += 2
    }
    console.log(arr);
}
var arr;
arr = [1,2];
f1(arr);
console.log(arr);


// 5、
// 两个对象是同一个对象,不同的操作有什么不同
var a = [1,2];
var b = a;
a[0] = 20;    // 如果a = [20,2];会怎么样
console.log(b);


//6、
function Person(name, age, salary) {
    this.name = name;
    this.age = age;
    this.salary = salary;
}
function f1(pp) {
    pp.name = "ls";
    pp = new Person("aa", 18, 10);
}
var p = new Person("zs", 18, 1000);
console.log(p.name);
f1(p);
console.log(p.name);
console.log(pp.name);

8 数据类型的本质

具有相同构造函数的对象,就是同一种数据类型。

results matching ""

    No results matching ""