《JavaScript面向对象精要》(上)

面向对象的语言有如下几种特性:

  • 封装: 数据可以和操作数据的功能组织在一起,这就是对象的定义
  • 聚合: 一个对象能够引用另一个对象
  • 继承:一个新创建的对象和另一个对象拥有同样的特性,而无需显示复制其功能
  • 多态:一个接口可被多个对象实现

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

1. 原始类型

  • 照原样保存简单数据

  • JavaScript有5种原始类型:boolean, number, string, null, undefined

  • 每个原始值的变量使用自己的存储空间,一个变量的改变不影响其他变量

    var color1 = "red";
    var color2 = color1;
    console.log(color1);    // "red"
    console.log(color2);    // "red"
    
    color1 = "blue";
    console.log(color1);     // "blue"
    console.log(color2);     // "red"
    
  • 鉴别原始类型 — typeof

    console.log(typeof "Nicholas")    // "string"
    console.log(typeof 10)            // "number"
    console.log(typeof true)          // "boolean"
    console.log(typeof undefined)     // "undefined"
    console.log(typeof null)          // "object" null为空对象指针,返回"object"
    

    判断一个值是否为空类型的最佳方法是直接和null比较

    console.log(value === null)      // true or false
    
  • 非强制转换比较

    • 三等号比较:不会将变量强制转换为另一种类型

    • 双等号比较:将变量强制转换成另一种类型

    console.log(undefined == null);   //true
    console.log(undefined === null);  //false
    
  • 原始方法

var count = 10;
count.toFixed(2);                 // 10.00
var flag = true;
flag.toString();                  // true  
var name = "Nicholas"
name.toLowerCase();               // nicholas

原始类型有方法,但它们不是对象,JavaScript使它们看上去像对象,提供语言上的一致性体验

2.引用类型

  • 概述:指JavaScript中的对象,最接近类的东西

    • 引用值: 引用类型的实例,是对象的同义词
    • 属性:包含键(始终是字符串)和值,对象是属性的无序列表
    • 方法:属性的值为函数,该属性被称为对象的方法
  • 创建对象:

    • new操作符和构造函数

      var object = new Object();  // object 是指向内存中实际对象所在位置的指针(或引用)
      
    • 对象字面形式: {key: value, key: value}

      JavaScript引擎背后做的工作和new Object()一样,其他引用类型字面形式也是如此

  • 对象引用解除:将对象变量置为null,让垃圾收集器释放内存

  • 函数字面形式与正则表达式字面形式

    function reflect(value){
        return value;
    }
    // is the same as
    var reflect = new Function('value','return value;');
    
var numbers = /\d+/g;
// is the same as
var numbers = new RegExp('\\d+','g');   // 传入模式参数是字符串,对反斜杠进行转义
  • 访问属性 :点号和中括号(允许在属性名字上使用特殊字符)

  • 鉴别引用类型:instanceof操作符,若对象是构造函数所指定的类型的一个实例,返回true

    instanceof 操作符可鉴别继承类型,所有对象都是Object的实例,所有引用类型都继承自Object

    var items = [];
    var obj = {};
    function reflect(value){
      return value;
    }
    console.log(items instanceof Array);  // true
    console.log(items instanceof Object); // true
    console.log(obj instanceof Object);   // true
    console.log(obj instanceof Array);    // false
    console.log(reflect instanceof Function); // true
    console.log(reflect instanceof Object);   // true
    
  • 鉴别数组: Array.isArray(),鉴别一个值是否是数组的实例

    var items = [];
    console.log(Array.isArray(items))  // true 
    

    ie8或更早版别不支持该方法

  • 原始封装类型: String 、Number和Boolean

    • 读取字符串、数字或布尔值,原始封装类型将被自动创建
    • 自动创建的实体仅用于该语句,并随后被销毁
    var name = "Nike";
    var firstChar = name.charAt(0);
    console.log(firstChar);
    
    name.last = "zalas";
    console.log(name.last)        // undefined
    //这是在背后发生的事情
    var name = "Nike";
    var temp = new String(name);
    var firstChar = temp.charAt(0);
    temp = null;
    console.log(firstChar);
    
    var temp = new String(name);
    console.log(temp.last);       // undefined
    temp = null;                 
    
    • instanceof 检查对应类型返回false(未读取任何东西,未创建临时对象)
    var name = 'nike';
    var count = 10;
    var found = false;
    console.log(name instanceof String);    // false
    console.log(count instanceof Number);   // false
    console.log(found instanceof Boolean);  // false
    
    • 手动创建原始封装类型会创建出object,typeof无法鉴别出实际保存的数据类型
    var name = new String('nike');
    var count = new Number(10);
    var found = new Boolean(false);
    console.log(typeof name);         // "object"
    console.log(typeof count);        // "object"
    console.log(typeof found);        // "object"
    

    使用String,Number和Boolean对象和使用原始值有一定区别

    var found = new Boolean(false);
    if(found){
        console.log("Found")
    }
    

第二章 函数

定义:函数是对象,不同于其他对象的决定性特点是存在[[Call]]内部属性,内部属性无法通过代码访问,而是定义了代码执行的行为。

1.函数字面形式: 声明或表达式

函数声明会被提升至上下文(所在函数的范围或全局范围)的顶部

//1.函数声明
function add(num1 + num2){
    return num1 + num2;
}
//2.函数表达式:function关键字后不需要加函数名字,通常会被一个变量或属性引用
var add = function(num1, num2){
    return num1 + num2;
}

2. 函数就是值

只要可以使用其他引用值的地方,就可以使用函数

var numbers = [1,2,5,8,10,6];
numbers.sort(function(first, second){
    return first - second;
});

3.参数

  • JS函数独特之处:给函数传递任意数量参数不造成错误,

    因为参数实际被保存在一个称为arguments的类似数组的对象中。

    arguments的length可得知当前参数个数

函数期望参数个数保存在函数的length中,实际传入数量不影响期望参数个数

arguments 对象不是数组的实例,Array.isArray(arguments)永远返回false

function reflect(value){          // value 为命名参数
    console.log(arguments.length) // 2
    return value;
}
console.log(reflect.length)    // 1 期望参数

reflect = function(){
    return arguments[0];
};
console.log(reflect('hi',25));    // "hi"
console.log(reflect.length);      // 0

在不知道有多少参数情况下,使用arguments比命名参数有效

function sum(){
    var result = 0,
        i = 0,
        len = arguments.length;
    while (i < len){
        result += arguments[i];
        i++;
    }
    return result;
}

console.log(sum(1,2));       // 3
console.log(sum(50));       // 50
console.log(sum(0));       // 0

4. 重载

js 不存在函数签名,也不存在重载,可模仿函数重载

function sayMessage(message){
  if(arguments.length === 0){    //  message == undefined更常见
      message = "Default message";
  }
    console.log(message);
}
sayMessage("hello" )

5. this 对象

  • 解决方法和对象间的紧耦合

  • JS所有函数作用域内都有一个this对象代表调用该函数的对象

    var person = {
        name: 'nike',
        sayName: function(){
          console.log(person.name); 
            //若person变量名改变,方法中引用名也要改,换成this解决紧耦合
        }
    };
    person.sayName();              
    
  • 改变this方法

    • call(): 多个参数,this值和所有参数
    • apply():2个参数,this值和一个数组或类似数组的对象
    • bind():传给新函数的this的值,其他所有参数代表需要被永久设置在新函数中的命名参数
    function sayNameForAll(label){
      console.log(label + ":" + this.name);
    }
    var person1 = {
        name: 'nike'
    };
    var name = 'michael';
    sayNameForAll.call(this, 'global');
    //sayNameForAll.apply(this, ['global']); 其他与call一样
    sayNameForAll.call(person1, 'person1');
    //sayNameForAll.apply(person1, ['person1']);
    
    function sayNameForAll(label){
      console.log(label + ":" + this.name);
    }
    var person1 = {
        name: 'nike'
    };
    
    var sayNamePerson1 = sayNameForAll.bind(person1);
    sayNamePerson1('person1');            //  "person1:nike"
    
    var sayNamePerson2 = sayNameForAll.bind(person2, 'person2');
    sayNamePerson2();                     //   "person2:nike"
    

第3章 理解对象

1.属性探测

  • in 操作符:检查自有属性和原型属性

  • hasOwnProperty()方法:检查自有属性存在

    var person1 = {
        name: 'nike',
        sayName: function(){
            console.log(this.name);
        }
    };
    
    console.log("name" in person1);          // true
    // 同样可以检查一个方法是否存在
    console.log("sayName" in person1);       // true
    console.log("toString" in person1);      // true
    
    console.log(person1.hasOwnProperty("name"));      // true
    console.log(person1.hasOwnProperty("toString"));  // false
    

2. 删除属性 —— delete

设置一个属性的值为null不能将它从对象中彻底移除,需要用delete操作符,成功时返回true。可理解为在哈希表中移除了一个键值对。

var person1 = {
    name: 'nike'
};
delete person1.name;                         // true - not output
console.log(person1.name);                   // undefined

3. 属性枚举

可枚举属性的内部特征[Enumerable]被设置为true,可用for-in循环遍历它们。

以下两个方法,可获取对象的可枚举属性

  • for-in循环

    • 遍历对象的所有可枚举属性并将属性名赋给一个变量

    • 会遍历原型属性

      var property;
      for(property in object){
         console.log("name:" + property);
         console.log("value:" + object[property]);
      }
    
  • Object.keys()

    • 获取可枚举属性名字的数组,再用for循环遍历属性并输出他们的名字和值
    • 只返回自有(实例)属性
    var properties = Object.keys(object);
    var i,len;
    for(i = 0,len = properties.length; i < len; i++){
        console.log("name:" + properties[i]);
        console.log("value:" + object[properties[i]]);
    }
    

propertyIsEnumerable()方法检查一个属性是否为可枚举的

var person1 = {
    name: 'nike'
};
console.log(person1.propertyIsEnumerable("name"));    // true

var properties = Object.keys(person1);
console.log(properties.propertyIsEnumerable("length")); // false

所有添加的属性默认是可枚举的,对象的大部分原生属性默认是不可枚举的

4.属性类型和特征

属性有两种类型: 数据属性和访问器属性

  • 数据属性:包含一个值

  • 访问器属性:不包含值,定义了一个当属性读取时调用的函数(getter)和当属性被写入时调用的函数(setter),两者其一便可

    var person1 = {
        _name: 'nike',
        get name() {
            console.log('reading');
            return this._name;
        },
        set name(value) {
            console.log('setting');
            this._name = value;
        }
    };
    console.log(person1.name);            // "nike"       
    person1.name = "greg";
    console.log(person1.name);            // "greg"
    

    仅定义getter,该属性就变成只读,仅定义setter,该属性就变成只写

  • 通用特征:数据和访问器属性都有的特征

    • [[Configurable]]:决定该属性是否可配置
  • 改变属性特征:Object.defineProperty(),该方法有3个参数

    • 拥有该属性的对象

    • 属性名

    • 包含需要设置的特征的属性描述对象

    var person1 = {
      name: 'nike'
    };
    Object.defineProperty(person1, 'name', {
        enumerable: false
    });
    
    console.log('name' in person1);                      // true
    console.log(person1.propertyIsEnumerable('name'));   // false
    
  • 数据属性特征

    var person1 = {
        name: 'nike'
    };
    //is the same as
    var person1 = {};
    Object.defineProperty(person1, 'name',{
        value: 'nike',
        enumerable: true,
        configurable: true,
        writable: true
    });
    

    当用Object.defineProperty()定义新属性时一定要指定所有特征的值,否则布尔型的特征会默认设置为false

  • 访问器属性特征: 不需要存储值

    • [[Get]]
    • [[Set]]
    var person1 = {
        _name: 'nike',
        get name(){
            console.log('reading');
            return this._name;
        },
        set name(value){
           console.log('setting');
           this._name = value;
        }
    };
    // is the same as
    var person1 = {
        _name: 'nike'
    };
    Object.defineProperty(person1, 'name', {
        get: function() {
            console.log('reading');
            return this._name;
        },
        set: function(value) {
            console.log('setting');
            this._name = value;
        },
        enumerable: true,
        configurable: true
    })
    
  • 定义多重属性:defineProperties(),接受两个参数:

    • 需要改变的对象
    • 一个包含所有属性信息的对象
    var person1 = {};
    Object.defineProperties(person1, {
        _name: {
            value: 'nike',
            enumerable: true,
            configurable: true,
            writable: true
        },
        name: {
            get: function(){
                console.log('reading');
                return this._name;
            },
            set: function(value){
                console.log('setting');
                this._name = value;
            },
            enumerable: true,
            configurable: true
        }
    });
    
  • 获取属性特征—— Object.getOwnPropertyDescriptor()

    • 只可用于自有属性

    • 接受两个参数:对象和属性名

    • 若属性存在,返回一个属性描述对象,内含4个属性:configurable和

      enumerable,另两个属性根据属性类型决定

    var person1 = {
        name: 'nike'
    };
    
    var descriptor = Object.getOwnPropertyDescriptor(person1, 'name');
    console.log(descriptor.enumerable);                 // true
    console.log(descriptor.configurable);               // true
    console.log(descriptor.writable);                   // true
    console.log(descriptor.value);                      // 'nike'
    

5. 禁止修改对象

[[Extensible]] 指导对象行为的内部特征,指明该对象本身是否可以被修改

设置[[Extensible]]为false,就能禁止新属性的添加

  • 禁止扩展:用Object.preventExtensions()创建一个不可扩展的对象。
    • 该方法接受一个参数,是希望使其不可扩展的对象
    • 一旦使用此方法,永远不能再添加新属性
    • 可用Object.isExtensible()来检查[[Extensible]]的值
var person1 = {
    name: 'nike'
};
console.log(Object.isExtensible(person1));         // true

Object.preventExtensions(person1);
console.log(Object.isExtensible(person1));         // false

person1.sayName = function(){
    console.log(this.name);
};
console.log("sayName" in person1);                 // false
  • 对象封印:

    • 一个被封印的对象是不可扩展的且其所有属性都不可配置

    • 如果一个对象被封印,只能读写它的属性

    • 可用Object.seal()来封印对象,调用后[[Extensible]]特性被置为false,

      其所有属性的[[Configurable]]特性被置为false

    • 用Object.isSealed()判断一个对象是否被封印

var person1 = {
    name: 'nike'
};
console.log(Object.isExtensible(person1));           // true
console.log(Object.isSealed(person1));               // false

Object.seal(person1);
console.log(Object.isExtensible(person1));           // false
  • 对象冻结:被冻结的对象是一个数据属性都为只读的被封印对象
    • 被冻结对象无法解冻
    • Object.freeze()来冻结一个对象
    • 用Object.isFrozen()来判断一个对象是否被冻结
var person1 = {
    name: 'nike'
};
console.log(Object.isExtensible(person1));           // true
console.log(Object.isSealed(person1));               // false
console.log(Object.isFrozen(person1));               // false

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

推荐阅读更多精彩内容

  • 尽管javascript里有大量内建引用对象,很可能你还说会频繁创建自己的对象。当你在这么做的时候,记得javas...
    WanLum阅读 507评论 1 3
  • 第3章 基本概念 3.1 语法 3.2 关键字和保留字 3.3 变量 3.4 数据类型 5种简单数据类型:Unde...
    RickCole阅读 5,000评论 0 21
  • 工厂模式类似于现实生活中的工厂可以产生大量相似的商品,去做同样的事情,实现同样的效果;这时候需要使用工厂模式。简单...
    舟渔行舟阅读 7,615评论 2 17
  • 布尔操作符!false //true!"blue" //false!0 //true!NaN //true!...
    左钱钱阅读 357评论 0 0
  • 骄阳升雨后,万里遍霞虹。 天地皆春色,乾坤沐惠风。 争鲜千叶洗,斗艳百花隆。 放眼登高处,鄱湖一碧中。 ...
    雨后晴虹阅读 98评论 0 0