zoukankan      html  css  js  c++  java
  • JavaScript 系列博客(四)

    JavaScript 系列博客之(四)

    前言

    本篇介绍 JavaScript 中的对象。在第一篇博客中已经说到 JavaScript 是一种‘’对象模型‘’语言。所以可以这样说,对象是 JavaScript 语言的核心概念,也是最重要的数据类型。

    概述

    生成方法

    在 JavaScript 中声称对象相当方便,直接定义一个空字典就 ok。想要添加属性或者方法的话可以在定义结束之后动态添加。注意:对象时无序的复合数据集合。

    上面代码中,大括号就可以直接定义一个对象,被赋值给变量 a,所以 a 就指向一个对象。该对象为一个空对象,但是会有一些默认的方法,像 constructor 是构造方法,想要动态的添加属性和方法就是这个方法的功劳。

    在这里添加了一个属性为name,那么 name 是键名(成员名称),字符串 musibii 是键值(成员的值)。键名与键值之间用冒号分隔。如果再添加一个属性,那么属性之间使用逗号分隔。

    具体生成方法

    // 1.单一对象
    var obj = {
        // 属性
        name: 'Zero',
        // 方法
        teach: function () {
            console.log("教学");
        }
    };
    obj.name | obj.teach()
    
    // 2.构造函数
    function Person(name) {  // 类似于python中的类一样来使用
       	// this代表Person构造函数实例化出的所有具体对象中的某一个
        this.name = name;  
        this.teach = function () {
            console.log(this.name + "正在教学");
        }
    }
    // ①通过构造函数实例化出具体对象
    // ②通过对象.语法调用属性与方法
    var p1 = new Person("张三"); 
    p1.name  // 张三, this指向p1对象
    var p2 = new Person("李四"); 
    p2.teach  // 李四正在教学, this指向p2对象
    
    // 3.ES6类语法
    class Student {
        // 需要构造器(构造函数)来完成对象的声明与初始化
        constructor (name) {
            // 属性在构造器中声明并完成初始化
            this.name = name;
        }
        // 类中规定普通方法
        study () {
            console.log(this.name + "正在学习");
        }
        // 类方法
        static fn() {
            console.log("我是类方法")
        }
    }
    // 类中的普通方法由类的具体实例化对象来调用
    // 类中的类方法由类直接来调用(这类型的方法大多是功能性方法,不需要对象的存在)
    

    键名

    对象的所有键名都是字符串(ES6又引入了 Symbol 值也可以作为键名:还没了解过),所以加不加引号都可以。如果键名是数值,会被自动转为字符串。如果键名不符合标识名的条件(比如第一个字符为数字,或者含有空格或运算符),且也不是数字,则必须加上引号,否则会报错。

    对象的每一个键名又称为‘’属性‘’,它的键值可以是任何数据类型。如果一个属性的值为函数,通常把这个属性称为方法,调用方法和函数一样。

    特别的如果属性的值指向的还是一个对象,那么就行成了链式引用。对象的属性之间用逗号分隔,最后一个属性后面可以加逗号,也可以不加。

    对象的引用

    如果不同的变量名指向同一个对象,那么它们都是这个对象的引用,也就是说指向同一个内存地址。修改其中一个变量,会影响到其他所有的引用。和 JavaScript 中的基本数据类型不一样,复合数据类型是传址传递。

    a 和 b指向同一个对象,因此为其中任何一个变量添加属性,另一个变脸都可以读写该属性。如果取消某一个对象的引用,不会影响到其他变量。

    这种引用只局限于对象,在之前的博客也提到,两个变量指向同一个原始类型(基本数据类型)的值,那么变量只是对值得拷贝(传值传递)。

    属性的操作

    属性的读取

    读取对象的属性,有两种方法,一种是使用点运算符;另一种是使用方括号运算符。(在 python 中,字典只能通过方括号取值;对象只可以通过点运算符取值。不过可以通过自定义字典类改写 getattr 魔术方法改变。)

    注意:如果使用方括号运算符,键名必须放在引号里面,否则会被当做变量处理。

    var foo = 'bar';
    
    var obj = {
        foo: 1,
        bar: 2
    };
    
    obj.foo // 1
    obj[foo]// 2
    

    上面代码中,引用对象obj 的 foo 属性时,如果使用点运算符,foo 就是字符串;如果使用方括号运算符,但是不使用引号,那么 foo 就是一个变量,指向字符串 bar。

    方括号运算符内部还可以使用表达式:

    obj['hello' + 'world']
    obj[3 + 3]
    

    数字键可以不加引号,因为会自动转为字符串。

    var obj = {
        0.7: 'hello world'
    };
    obj['0.7'] // 'Hello World'
    obj[0.7] // 'Hello World'
    

    上面代码对象的数字键0.7加不加引号都可以,因为会自动转为字符串。

    var obj = {
        123: 'Hello musibii'
    };
    
    obj.123 // 报错
    obj[123] // 'Hello musibii'
    

    如果对数值键名123使用点运算符,会报错,使用方括号运算符才是正确的方式。

    属性的赋值

    点运算符和方括号运算符,不仅可以用来读取值,还可以用来赋值。

    JavaScript 允许属性的后绑定,也就是说可以在任意时刻新增属性,没必要在定义对象的时候就把属性全都定义好。

    属性的查看

    查看一个对象的所有属性使用 Object.keys 方法。

    var obj = {
        key1: 1,
        key2: 2
    };
    
    Object.keys(obj); // [key1, key2]
    

    属性的删除

    delete 命令用于删除对象的属性,删除成功后返回 true。

    var obj = {
        p: 1;
    }
    
    delete obj.p; // true
    obj.p // undefined
    Object.keys(obj) // []
    

    上述代码中,delete 命令删除对象 obj 的 p 属性。删除后,再读取 p 属性就会返回 undefined,而且 Object.keys 方法的返回值也不再包括该属性。

    注意:删除一个不存在的属性,delete 不会报错而是返回 true。因此不能根据 delete 命令的结果认为某个属性的存在。(那么到底哪种方式才可以证明某个属性的存在与否)

    如果删除属性时返回 false那就说明该属性存在,但是不可以删除。

    var obj = Object.defineProperty({}, 'p', {
        value: 'musibii',
        configurable: false
    });
    
    obj.p // 'musibii'
    delete obj.p // false
    

    上述代码中,通过 Object 的defineProperty方法给对象 obj创建了一个属性,属性的configurable(可配置) 的值为 false,这样的一个属性就是不可以删除的。

    另外需要注意的是,delete 命令只能删除对象本身的属性,无法删除继承的属性。

    可以看出虽然 delete 命令返回 true,但是删除的属性依然存在。但是如果通过 proto 删除的话就可以删除。

    判断属性的存在

    in 运算符用于检出对象是否包含某个属性(注意,检查的是键名,不是键值)。如果包含就返回 true,否则就返回 false。它的左边是一个字符串,表示属性名,右边则是一个对象。

    var obj = {p: 1};
    'p' in obj //true
    'toString' in obj // true
    

    拿上面删除的 constructor 来说:

    in 运算符的一个问题是,它不能识别哪些属性时对象自身的,哪些属性是继承的。就像上面,对象 obj 本身并没有 toString 属性,但是 in 运算符会返回 true,因为这个属性是继承的。

    这时可以通过对象的 hasOwnProperty 方法判断,是否为对象自身属性

    var obj = {};
    if ('toString' in obj) {
        console.log(obj.hasOwnProperty('toString')); // fasle
    }
    

    属性遍历

    for...in 循环用来遍历一个对象的所有属性。

    for...in 循环有两个注意点;

    • 它遍历的是对象所有可遍历的属性,会跳过不可遍历的属性;
    • 它不仅遍历对象自身的属性,还遍历继承的属性。

    如果继承的属性是可遍历的,那么就会被 for...in 循环遍历到。但是,一般情况下,都是只想遍历对象自身的属性,所以使用 for...in 的时候,应该结合使用 hasOwnProperty 方法,在循环内部判断一下,某个属性是否为对象自身的属性。

    var person = {name: '老张'};
    
    for (var key in person) {
        if (person.hasOwnProperty(key)) {
            console.log(key);
        }
    } // name
    

    with 语句

    with 语句的格式如下:

    with (对象) {
        语句;
    }
    

    它的作用是操作同一个对象的多个属性时,提供一些书写的方便。

    注意:如果 with 区块内部有变量的赋值操作,必须是当前对象已经存在的属性,否则会创造一个当前作用域的全局变量。

    var obj = {};
    with (obj) {
        p1 = 4,
        p2 = 5
    }
    
    obj.p1 // undefined
    p1 // 4
    

    上面代码中,对象 obj 并没有 p1属性,对 p1赋值等于创造了一个全局变量 p1.正确的写法应该是,先定义对象 obj 的属性 p1,然后在 with 区块内操作它。

    这是因为 with 区块没有改变作用域,它的内部依然是当前作用域。这造成了with 语句的一个很大的弊病,就是绑定对象不明确。

    with (obj) {
        console.log(x);
    }
    

    单纯从上面的代码块,根本无法判断 x 到底是全局变量,还是对象 obj 的一个属性。这非常不利于代码的除错和模块化,编译器也无法对这段代码进行优化,只能留到运行时判断,这就拖慢了运行速度。因此,建议不要使用 with 语句,可以考虑用一个临时变量代替 with。

    with (obj1.obj2.obj3) {
        console.log(p1 + p2);
    }
    // 可以写为
    var temp = obj1.obj2.obj3;
    console.log(temp.p1 + temp.p2);
    
  • 相关阅读:
    最小的K个数
    CentOS 7 连接不到网络解决方法
    数组中超过一半的数字
    字符串的排列
    二叉搜索树与双向链表
    复杂链表的复制
    二叉树中和为某一数值的路径
    二叉搜索树的后序遍历序列
    从上到下打印二叉树
    java的图形文档
  • 原文地址:https://www.cnblogs.com/zuanzuan/p/10161642.html
Copyright © 2011-2022 走看看