• 读书笔记-你不知道的JS上-对象


    好想要对象···

     

      函数的调用位置不同会造成this绑定对象不同。但是对象到底是什么,为什么要绑定他们呢?(可以可以,我也不太懂)

      

    语法

      对象声明有两个形式:

      1、字面量 => var obj = { ... };

      2、构造形式 => var obj = new Object();

      两种形式生成的对象是一样的,唯一的区别是,在文字声明中可以添加多个键值对,构造形式只能逐个添加。

     

    类型

      JS中有六种主要类型:string、number、booleam、null、undefined、object。

      注意,简单数据类型本身并不是对象,null有时会被当成一种对象类型,但是这其实只是语言本身的一个bug,即对null执行typeof null时会返回字符串object。实际上,null本身是基本类型。(原理是这样的,不同的对象在底层都表现为二进制,在JS中二进制的前三位都为0的话会被判断为object类型,null的二进制表示是全0,所以执行时会返回object。)

      有一种常见的错误是‘万物皆对象’,这显示是错误的(我去,被老师骗了)。

      实际上,JS中有许多特殊的对象子类型,称之为复杂基本类型。

      函数就是对象的一个子类型(技术角度来讲叫可调用对象)。JS中的函数是‘一等公民’,因为它们本质上和普通对象一样,所以可以像操作其他对象一样操作函数。

      数组也是对象的一种类型。

    内置对象

      JS有一些对象子类型,通常被称为内置对象。

      包括String、Number、Boolean、Object、Function、Array、Date、RegExp、Error。

      内置对象可以当作构造函数来使用,从而可以构造一个对应子类型的新对象。

      如果用字面量形式创建number、string(var str = 'jimmy',num = 1),调用对象方法(length)时会自动转换成对象并调用方法,然后还原为基本数据类型返回,这个过程是不可见的。但是也没有必要为了性能特地通过构造形式声明基本数据,这种转换十分频繁,所以底层已经做了优化,用字面量更好更快。

      null和undefined没有对应的构造形式,只有文字形式。

      Date只有构造,没有文字形式。

      对于Object、Array、Function、RegExp来说,无论使用哪种形式出来的都是对象。

      Error对象很少在代码中显示创建,一般在抛出异常的时候被自动创建。也可以用new Error()这种构造形式创建,一般来说,用不着。

      报错误信息,vue源码中用了console.error(),如下代码:

        warn = function(msg, vm) {
            if (hasConsole && (!config.silent)) {
                console.error("[Vue warn]: " + msg + " " + (
                    vm ? formatLocation(formatComponentName(vm)) : ''
                ));
            }
        };

      专门用来生成错误信息的函数,jQuery源码中反正是没见着,尤大还是很贴心的。

    内容

      对象内容由键值对组成,键更像是一个指针,保存内容所在存储位置的地址。(ES6之前,不支持奇怪的键,所以只能用普通字符串定义键)

      可以用点访问和[]访问键,主要区别是点访问属性名必须满足标识符的命名规范,而[]可以接受任意形式的字符串。

      还有一个细微差别:

        function fn(a) {
            console.log(obj.a); //undefined
            console.log(obj[a]); //1
        }
        var obj = {
            ab: 1,
        }
        fn('ab');

      当访问的属性名作为参数传入函数时,使用[]访问才能正常得到结果。

      在对象中,属性名永远是字符串,如果使用其余类型的值作为属性名,会被强制调用toString方法。

        var obj = {}
        obj[true] = 1;
        obj[1] = 2;
        obj[obj] = 3;
        console.log(obj['true']); //1
        console.log(obj['1']); //2
        console.log(obj['[object Object]']); //3

      好玩!

    可计算属性名

      ES6中可以使用表达式计算属性名。

        var str = 'jimmy';
        var obj = {
            [str + 'a']: 1,
            [str + 'b']: 2
        }
        console.log(obj.jimmya); //1
        console.log(obj.jimmyb); //2

      

    属性和方法

      如果属性访问的是函数,一般称为方法访问。技术上来讲,函数不属于任何对象,所有对函数的访问只是引用而已。

      数组也可以随便添加属性,但是不要这样做,数组对特殊行为和用途进行了优化。

    复制对象

      对于JSON安全的对象来说,可以用下面方法来实现深复制:

        var newobj = JSON.parse(JSON.stringify(oldobj));

      浅复制比较简单,ES6中定义了Object.assign()方法。第一个参数是目标对象,之后还可以跟多个源对象。

      jQuery中实现深复制的代码如下:

        jQuery.extend = jQuery.fn.extend = function() {
            var options, name, src, copy, copyIsArray, clone,
                target = arguments[0] || {},
                i = 1,
                length = arguments.length,
                deep = false;
            //参数修正
            if (typeof target === "boolean") {
                deep = target;
                target = arguments[i] || {};
                i++;
            }
            if (typeof target !== "object" && !jQuery.isFunction(target)) {
                target = {};
            }
            if (i === length) {
                target = this;
                i--;
            }
            for (; i < length; i++) {
                if ((options = arguments[i]) != null) {
                    for (name in options) {
                        src = target[name];
                        copy = options[name];
                        if (target === copy) {
                            continue;
                        }
                        if (deep && copy && (jQuery.isPlainObject(copy) ||
                                (copyIsArray = jQuery.isArray(copy)))) {
                            if (copyIsArray) {
                                copyIsArray = false;
                                clone = src && jQuery.isArray(src) ? src : [];
                            } else {
                                clone = src && jQuery.isPlainObject(src) ? src : {};
                            }
                            target[name] = jQuery.extend(deep, clone, copy);
                        } else if (copy !== undefined) {
                            target[name] = copy;
                        }
                    }
                }
            }
            return target;
        };

    [[Get]]/取值操作

      属性访问有一个细节。

        var obj = {
            a: 1,
        }
        console.log(obj.a); //1

      在语言规范中,obj.a在obj上实际上是实现了[[Get]]操作。对象默认的内置[[Get]]操作首先在对象中查找是否有名称相同的属性,如果找到就返回这个属性的值。

      如果没有找到名称相同的属性,按照[[Get]]算法的定义会执行另外一种非常重要的行为,遍历原型链。

      如果在原型链的顶端也没有找到,会返回undefined。

    [[Put]]/赋值操作

      有[[Get]],就会有[[Put]]([[put]]没太懂为什么用这个词)。

      [[Put]]被触发时,实际行为取决于许多因素,包括对象是否已经存在这个属性。

      如果已经存在这个属性,[[Put]]算法大概如下:

      1、属性是否是访问描述符?如果是并且存在setter就调用setter。

      2、属性的描述符中writable是否是false?如果是,非严格模式忽略,严格模式报错。

      3、如果都不是,将该值设置为属性的值。

      如果不存在,[[Put]]操作会更复杂。

    Getter/Setter

      对象默认的[[Put]]和[[Get]]操作分别可以控制属性值的设置和获取。

      在ES5中可以使用getter和setter部分改写默认操作,但是只能应用在单个属性上,无法应用在整个对象上。getter是一个隐藏函数,会在获取属性值时默认调用。setter也是一个隐藏函数,会在设置属性值时调用。

      当你给一个属性定义getter、setter或者两者都有时,这个属性会被定义为‘访问描述符’。对于访问描述符来说,JS会忽略它们的value和writable特性,取而代之的是关心set和get特性。

        var obj = {
            get a() {
                return 2;
            }
        }
        console.log(obj.a); //2
        //未定义set方法 无效
        //即便有set 由于定义的get只会返回2 所以没有意义
        obj.a = 3;
        console.log(obj.a); //2
        Object.defineProperty(obj, 'b', {
            get() {
                return this.a * 2;
            },
            enumerable: true
        });
        console.log(obj.b); //4

      两种定义都会在对象中创建一个不包含值的属性,对于这个属性的访问会自动调用一个隐藏函数,它的返回值会被当作属性访问的返回值。

      

        var obj = {
            // 获取值
            get a() {
                return this.b;
            },
            // 赋值返回2倍的值
            set a(val) {
                return this.b = val * 2;
            }
        }
        obj.a = 2;
        console.log(obj.a); //4

      设置对应的set和get。

     

  • 相关阅读:
    libreoffice转PDF文件
    window端设置elasticsearch的堆内存大小
    kubeadm 部署 K8S 1.21.5
    GitLab缓存问题
    【博客园美化】参考资料
    【Qt】QObject::connect: Cannot queue arguments of type 'QVector<int>' Make sure 'QVector<int>' is registered using qRegisterMetaType()解决办法
    【jeecgboot】启动报错
    验证下MarkDown
    nginx代理转发因http_version导致lua读取文件不存在问题
    怎么看mac是arm64还是x64
  • 原文地址:https://www.cnblogs.com/QH-Jimmy/p/6475446.html
Copyright © 2020-2023  润新知