阅读 276

【读书笔记】JavaScript面向对象精要(上)

写在前面

本文章为对书《JavaScript面向对象精要》的读后总结。在阅读本文章前,我们先来整理一份思维导图,帮助我们快速浏览本文大概内容

第一章 原始类型和引用类型

1.1 类型的定义

在JavaScript中,存在两种类型

  • 原始类型(保存为简单数据值)
  • 引用类型(保存为对象,本质是指向内存位置的引用)

1.2 原始类型

1.2.1 关于原始类型的简介

在JavaScript中,共有五种原始类型

  • boolean:布尔值,值为true、false
  • number:数字,值为所有的整型、浮点数值
  • string:字符串,用单引号和双引号包住的字符
  • null:空类型,值仅有null
  • undefined:未定义,值仅有undefiend
<!--boolean-->
var isEnv = true;
var isEnv = false;

<!--number-->
var num = 1;
var cost = 1.2;

<!--string-->
var str = '我是字符串';
var name = "我也是字符串";

<!--null-->
var obj = null;

<!--undefined-->
var res = undefined;
var resg;
复制代码

在JavaScript中,一个变量等于另一个变量时,当改变其中一个变量的值,并不会影响另一个值,请看下面的例子

变量
num1 1
num2 1
var num1 = 1;
var num2 = num1;

num1 = 3;

console.log('=>结果:', num1, num2);  // =>结果: 3, 1
复制代码

1.2.2 原始类型的鉴别

对于鉴别原始类型的最佳方式是使用typeof操作符,例:

console.log(typeof 1);                // number
console.log(typeof 1.2);              // number
console.log(typeof 'typeof');         // string
console.log(typeof true);             // boolean
console.log(typeof false);            // boolean
console.log(typeof undefined);        // undefined
复制代码

关于对null的鉴别却有不同之处

console.log(typeof null);      // "object"
复制代码

这里对于null的判断,最好的方法是值直接与其进行判断

let res = null;
console.log(null === res);       // true

res = undefined;

console.log(null === res);       // false
console.log(null == res);        // true
复制代码

==时,两个值进行比较时会将变量强制转换为另一种类型,所以对nullundefined进行比较时,会出现结果为true的情况。

1.2.3 原始方法

原始类型中除了nullundefined都有着原始方法

    var name = "lily";
    var newName = name.charAt(0);
    console.log(newName);                      // l

    var count = 10;
    var newCount = count.toFixed(2);
    console.log(newCount);                     // 10.00
复制代码

1.3 引用类型

引用类型就是JavaScript中的对象,它是最接近类的东西。

JavaScript中,可以利用new操作符创建一个对象

const obj = new Object();
复制代码

对于对象的解除,最佳方法是将其值变为null

let obj = new Object();

obj = null;
复制代码

对象可以随时添加属性和删除属性

const obj = new Object();

obj.name = '小明';
obj.job = 'web前端开发';

console.log(obj);          // {"name":"小明","job":"web前端开发"}

delete obj.name;

console.log(obj);          // {"job":"web前端开发"}
复制代码

还可以实例化内建类型

const array = new Array();          // 数组
const date = new Date();            // 日期
const error = new Error();          // 错误
const func = new Function();        // 函数
const reg = new RegExp();           // 正则
复制代码

我们依然可以使用字面形式创建

const obj = {};                   // 对象
const arr = [];                   // 数组

function func({                 // 函数

}

const reg = /\d+/g;               // 正则
复制代码

1.4 访问对象的属性

在JavaScript中,访问对象的属性有两种方法

  • "."的形式
  • "[]"的形式
const obj = {
    name'小明',
    job'web前端开发',
}

console.log(obj.name, obj.job);                 // 小明,web前端开发

console.log(obj["name"], obj["job"]);           // 小明,web前端开发
复制代码

1.5 鉴别引用类型

对于引用类型的鉴别,function类型可以使用typeofinstanceof方法,其余的类型要用instanceof

// function
function func({
    return 1;
}

console.log(typeof func);     // function

// Array
const array = [];

console.log(array instanceof Array);     // true

// Object
const obj = {};

console.log(obj instanceof Object);      // true
console.log(obj instanceof Array);       // false
console.log(array instanceof Object);    // true

// function
console.log(func instanceof Function);
console.log(func instanceof Object);     // true
复制代码

1.6 鉴别数组

鉴别数组可用Array.isArray()方法

const array = [];

console.log(Array.isArray(array));   // true
复制代码

1.7 原始封装类型

原始封装类型有三种:StringNumberBoolean

第二章 函数

2.1 函数的介绍

函数其实就是对象,函数具有两种字面形式

  • 函数声明(可提升至上下文)
func(1);

function func(data{
    console.log(data);
}
复制代码
  • 函数表达式(不可提升)
func(1);

const func = function(data{
  console.log(data);
}
复制代码

函数也是值,可以将它们赋给变量使用。

2.2 函数的参数

函数可以接收参数,我们可以给函数传递参数

函数可以接收的参数,可以使用arguments类数组对象接收

function func({
    console.log(arguments[0]);             // 1
    console.log(arguments.length);         // 5
}

func(12345);
复制代码

函数还可以正常接收参数,然后对参数进行处理

function func(a, b{
    return a + b;
}

console.log(func(12));    // 3
复制代码

2.3 函数的重载

JavaScript语言根据实际传入的采纳数决定调用函数的哪个版本,JS的函数不存在函数重载

function func(data{
    return data;
}

function func({
    return 'chongzai'
}

console.log(func('hanshu'));    // 'chongzai'
复制代码

但是,我们依然可以用JS去模拟函数重载

function func(data{
    if(arguments.length === 0) {
        data = 1;
    }
    return data;
}

console.log(func(2));                 // 2
复制代码

2.4 对象方法

如果属性的值是函数,那么这个属性就称为方法

var person = {
    name'小明',
    sayNamefunction({
        return this.name;
    }
}

console.log(person.sayName());   // "小明"
复制代码

2.4.1 this对象

JavaScript所有的函数作用域内都有一个this对象代表调用该函数的对象。在全局作用域中,this代表全局对象(浏览器中的window)

上例中,我们就用到了this方法,我们可以再写一个小例子理解一下this

var name = '熊大';

var person = {
    name"熊二",
    sayNamefunction({
        return this.name;
    }
}

console.log(person.sayName());           // "熊二"
var sayHello = person.sayName;
console.log(sayHello());                 // "熊大"
复制代码

2.4.2 改变this指向

1、call()

call()方法的第一个参数是this所指向的值,后边的参数就是需要传入函数的参数

var name = "光头强";

var person1 = {
    name"熊大"
}

var person2 = {
    name"熊二"
}

function sayName(type{
    return `${this.name}${type}`
}

console.log(sayName.call(person1, '好人'))    // 熊大是好人
console.log(sayName.call(person2, '好人'))    // 熊二是好人
console.log(sayName.call(this'坏人'))       // 光头强是坏人
复制代码

2、apply()

apply()方法和call()方法的区别就是:apply()第二个参数是传给函数的参数,它是一个数组

var name = "光头强";

var person1 = {
    name"熊大"
}

var person2 = {
    name"熊二"
}

function sayName(type{
    return `${this.name}${type}`
}

console.log(sayName.call(person1, ['好人']))    // 熊大是好人
console.log(sayName.call(person2, ['好人']))    // 熊二是好人
console.log(sayName.call(this, ['坏人']))       // 光头强是坏人
复制代码

3、bind()

bind()的第一个参数是要传给新函数的this的值,其他所有参数代表需要被永久设置在新函数中的命名参数

var name = "光头强";

var person1 = {
    name"熊大"
}

var person2 = {
    name"熊二"
}

function sayName(type{
    return `${this.name}${type}`
}

var sayName1 = sayName.bind(person1);
console.log(sayName1('好人'));                     // 熊大是好人

var sayName2 = sayName.bind(person2);
console.log(sayName2('好人'));                     // 熊二是好人
person2.sayName = sayName1;
console.log(person2.sayName("好人"));              // 熊大是好人
复制代码

第三章 理解对象

3.1 定义属性

// 第一种方式,创建对象时直接定义
var person = {
    name'熊大',
}

// 第二种方式,创建对象之后定义(以下两种创建对象方式等价)
var person1 = {};
var person1 = new Object();

person.age = 22;
person1.name = '熊大';
person1.age = 24;
person1.age = 25;

console.log(person);                // {"name": "熊大", "age": 22}
console.log(person1);               // {"name": "熊大", "age": 25}
复制代码

定义一个对象中没有的属性的时候,JS会调用[[put]]方法,当为对象中已经拥有的属性赋新值的时候,JS会调用[[set]]方法

3.2 属性探测

属性探测的三种方式

  • if判断
var person = {name'熊大'age0};
if(person.name) {
    console.log('找到啦');
}
复制代码

这种方式存在的问题:当if中判断的值是对象、非空字符串、非零数字或true时,返回true,当判断的值是nullundefined0falseNaN或空字符串时会返回false。但是对象中可包括价值,比如例子中,age的属性值为0,那么判断就会返回false,即使包含了age属性,也不会检测到,所以这种探测属性的方式并不推荐

  • in操作符
var person = {
    name'熊大'
    age0,
    sayNamefunction({
        return this.name + this.age;
    }
};

console.log("name" in person);      // true
console.log("age" in person);       // true
console.log("job" in person);       // false
console.log("sayName" in person);   // true
console.log("toString" in person);  // true
复制代码

这种方式存在的问题:in操作符会检查自由属性和原型属性,可大多数时候我们可能只是想探测自由属性是否存在。所以我们会使用所有对象都拥有的hasOwnProperty()方法探测

  • hasOwnProperty
var person = {
    name'熊大'
    age0,
    sayNamefunction({
        return this.name + this.age;
    }
};

console.log(person.hasOwnProperty("name"));      // true
console.log(person.hasOwnProperty("age"));       // true
console.log(person.hasOwnProperty("job"));       // false
console.log(person.hasOwnProperty("sayName"));   // true
console.log(person.hasOwnProperty("toString"));  // false
复制代码

3.3 删除属性

设置一个属性的值为null,并不能完全删除属性,它只是将一个属性赋值为了null,我们需要用delete去操作。

var person = {
    name'熊大',
    age11,
    sayNamefunction({
        return this.name + this.age;
    }
}

delete person.name;
delete person.sayName;

console.log(person);         // {age: 11}
复制代码

3.4 属性枚举

我们自己添加的属性默认是可枚举的,枚举属性的内部特征[[Enumerable]]true。我们有两种方式可枚举属性:

  • for-in[同时遍历自有属性和原型属性]
var person = {
    name'熊大',
    age11,
}

for(var key in person) {
    console.log(key, person[key]);   // name, '熊大'  age, 11 
}
复制代码
  • Object.keys()[只遍历自有属性]
var object = Object.keys(person);

for(var i = 0;i < object.length;i++) {
    var key = object[i];
    console.log(key, person[key]);        // name, '熊大'  age, 11
}
复制代码

我们要注意,不是所有的属性都是可枚举的,对象的大部分原生方法的[[Enumerable]]特征都被设置为false,我们可以用propertyIsEnumerable()方法检测该属性是否可枚举

var person = {
    name'熊大'
}

console.log(person.propertyIsEnumerable("name"));          // true
console.log(person.propertyIsEnumerable("toString"));      // false
复制代码

3.5 属性类型

  • 数据属性:包含一个值
  • 访问器属性:读取函数getter和属性写入函数setter
var person = {
    _name: '熊大',

    get name({
        console.log('get');
        return this._name;
    },

    set name(value{
        console.log('set');
        this._name = value;
    }
}

console.log(person.name);

person.name = '熊二';

console.log(person);
复制代码

从输出的结果看,我们取name值的时候用到了get方法,设置name值的时候,用到了set方法。

3.6 属性特征

3.6.1 通用特征

  • [[Enumerable]]:决定是否可以遍历属性
  • [[Configurable]]:决定了该属性是否可配置
var person = {
    name'熊大',
}

Object.defineProperty(person, "name", {             // 设置name属性不可遍历
    enumerable: false
})

console.log(person.hasOwnProperty("name"));        // true
console.log(person.propertyIsEnumerable("name"));  // false

Object.defineProperty(person, "name", {            // 设置name属性不可配置
    configurable: false
})

delete person.name;

console.log(person.name);

Object.defineProperty(person, "name", {           // Uncaught TypeError: Cannot redefine property: name
    configurable: true
})
复制代码

3.6.2 数据属性特征

  • [[Value]]:属性的值
  • [[Writable]]:属性是否可写入
var person = {}

Object.defineProperty(person, "name", {
    enumerabletrue,
    configurabletrue,
    value'熊大',
    writabletrue,
})

console.log(person);                  // {name: '熊大'}
复制代码

调用Object.defineProperty()时,会先检查属性是否存在,当不存在时,会根据我们指定的特征创建这个属性。我们在指定特征的时候要注意:如果不给属性指定特征,那么这个属性默认的特征的false

var person = {};

Object.defineProperty(person, "name", {
    value'熊大'
})

console.log(person.name);                            // 熊大
console.log(person.propertyIsEnumerable("name"));    // false

delete person.name;
console.log(person.hasOwnProperty("name"));          // true

person.name = '熊二';
console.log(person.name);                            // 熊大
复制代码

上例中创建的name属性就是不可遍历、不可配置、不可写入的。

3.6.3 访问器属性特征

  • [[get]]:取某个属性的值
  • [[set]]:给属性赋值

不能创建一个同时具有数据特征和访问器特征的属性

var person = {
    _name'熊大'
}

Object.defineProperty(person, "name", {
    getfunction({
        console.log('get');
        return this._name;
    },
    setfunction(value{
        console.log('set');
        this._name = value;
    },
    enumerabletrue,
    configurabletrue,
})

console.log(person.propertyIsEnumerable("name"));      // true
console.log(person.name);                              // get 熊大

person.name = '熊二';                                  // set
console.log(person.name);                              // get 熊二

delete person.name;                              
console.log(person.name);                              // undefined
复制代码

3.6.4 定义多重属性

使用Object.defineProperties()可以为一个对象定义多个属性。

var person = {};

Object.defineProperties(person, {
    name: {
        value"熊大",
        enumerabletrue,
        confogurabletrue,
        writabletrue,
    },
    age: {
        value11,
        enumerabletrue,
        confogurabletrue,
        writabletrue,
    }
})

console.log(person);            // {name: '熊大', age: 11}
console.log(person.name);       // 熊大
console.log(person.age);        // 11
复制代码

3.6.5 获取属性的特征

我们定义好的属性特征,还可以用Object.getOwnPropertyDescriptor()方法获取

var person = {name: '熊大'}

var descriptor = Object.getOwnPropertyDescriptor(person, "name");

var { enumerable, configurable, writable, value } = descriptor;

console.log(enumerable, configurable, writable, value);  // true true true '熊大'
复制代码

3.7 禁止修改对象

[[Extensible]]表示该属性是否可被修改

3.7.1 禁止扩展

Object.preventExtensions()方法创建一个不可扩展的对象,这就不可以为对象新增属性了。用Object.isExtensible()方法检查[[Extensible]]的值

var person = {name'熊大'};

console.log(Object.isExtensible(person));      // true

Object.preventExtensions(person)

person.age = 11;

console.log(person);                          // {name: '熊大'}
console.log(person.age);                      // undefined

console.log(Object.isExtensible(person));     // false
复制代码

3.7.2 对象封印

对象封印也可以创建一个不可扩展的对象,被封印的对象不可扩展并且所有属性都不可配置。对象封印使对象不能添加属性,也不能删除、改变类型(只能读写)

Object.seal()方法封印对象,Object.isSealed()判断一个对象是否被封印

var person = {
    name'熊大'
}

console.log(Object.isSealed(person));            // false

Object.seal(person);

person.age = 11;
person.name = '熊二';

console.log(person.name);                       // 熊二

delete person.name;

console.log(Object.isSealed(person));           // true
console.log(person);                            // {name: '熊二'}
复制代码

3.7.3 对象冻结

对象冻结也是扩展对象的一种方法,一个被冻结的对象只为可读,被冻结的对象无法解冻,用Object.freeze()方法冻结一个对象,用Object.isFrozen()来判断对象是否冻结

var person = {
    name'熊大'
}

console.log(Object.isFrozen(person));          // false

Object.freeze(person);
console.log(Object.isFrozen(person));         // true

person.age = 11;
person.name = '熊二';

delete person.name;

console.log(person);                         // {name: '熊大'}  
复制代码

总结

本文主要整理了《JavaScript面向对象精要》中的前三章内容,如有不对之处,还请大家指出~

下一篇文章将会对本书的后三章进行整理,目录如下:

  • 第四章 构造函数和原型对象
  • 第五章 继承
  • 第六章 对象模式

最后,分享一波我的个人公众号「web前端日记」,欢迎大家扫描下方的二维码前来关注~

关注下面的标签,发现更多相似文章
评论