js对象

对象的概述

什么是对象?

对象是一个具体的事物,包含一系列的属性,这些属性是无序的,每一属性都有一个字符串key和对应的value

在js语言中,除了基本数据类型剩下的就是对象了

对象原型上的属性

Object.prototype上的属性
  1. obj.constructor ; 指向创建该对象的构造函数
  2. obj.hasOwnProperty(propertyName); 返回一个boolean类型的值
  3. obj.isPrototypeOf(obj2); 判断obj是不是obj2的原型
  4. obj.propertyIsEnumerable(propertyName)
  5. obj.toLocaleString();
  6. obj.toString();
  7. obj.valueOf();
属性操作

读写对象属性 : 读取对象obj.z; 写对象obj.z=3; 循环读写对象属性obj[string]

属性异常

删除属性 : delete obj.z

检测属性 : hasOwnProperty 或 in

枚举属性 : obj.propertyIsEnumerable(propertyName), 一个boolean值

设置枚举属性 : Object.defineProperty(obj, "price", {enumerable : false, value : 1000});为obj设置一个不可枚举的属性price

  • 判断对象是否拥有某个属性obj.hasOwnProperty(propertyName) 该方法不判断原型链上的属性,只判断自身拥有的属性,自身拥有即为true,否则为false
  • 判断对象或对象的原型链上是否拥有某一属性可以使用in,如console.log("name" in obj) ,如果在原型链上能找到name属性即返回true,反之为false

  • 判断是否可以使用delete关键字删除对象的属性,可以查看对象的自身属性描述中的configurable是否为true,当描述对象中的configurable值为true时才可以删除对象的属性。

    delete Object.prototype; //false
    var descriptor = Object.getOwnPropertyDescriptor(Object,"prototype");
    console.log(descriptor);
    /*
    configurable: false
    enumerable: false
    value: {constructor: ƒ, __defineGetter__: ƒ, __defineSetter__: ƒ, hasOwnProperty: ƒ, __lookupGetter__: ƒ, …}
    writable: false
    __proto__: Object
    */
    descriptor.configurable;  //false,代表不允许删除或删除失败
    
    //定义的全局变量也不能通过delet删除
    var globalVal = 1;
    delete globalVal; //false
    
    // Object.defineProperty(obj, propertyName, {});
    // Object.defineProperty(为某个要添加属性的对象, 要添加的属性名, 对将被修改或定义的属性的描述);
    var obj = {}
    Object.defineProperty(obj, 'li', {value: '厉害了...'});
    

对象的描述符

通过Object.defineProperty(obj, propertyName, descriptor);

如下的属性都是描述符对象(descriptor)的key

  1. writable 可写的
  2. enumerable
  3. configurable
  4. value
  5. get
  6. set
对象属性实例:
var obj = {x:1};
console.log(obj.y); //undefined
var yz = obj.y.z;   //TypeError:Cannot read property "z" of undefined
obj.y.z = 2;        //TypeError:Cannot set property "z" of undefined

var yz = obj && obj.y && obj.y.z; //当obj、obj.y、obj.y.z都存在时,给yz赋值

//---------------------------------------------------------------------------------
var cat = new Object();
cat.legs = 4;
cat.name = "kitty";

console.log("legs" in cat); //true
console.log("abc" in cat);  //false
console.log("toString" in cat);//true

console.log(cat.hasOwnProperty("legs"));        //true
console.log(cat.hasOwnProperty("toString"));    //false

console.log(cat.propertyIsEnumerable("legs"));      //true
console.log(cat.propertyIsEnumerable("toString"));  //false

Object.defineProperty(cat,"price",{enumerable:false, value:1000});
console.log("price是否可枚举: "+cat.propertyIsEnumerable("price"));  //false

//---------------------------------------------------------------------------------
//过滤原型链上的属性
var o = {x:1,y:2,z:3};
var obj = Object.create(o);
obj.str = "我是自身属性";
for(var key in obj) {
  //console.log(key);   // 会打印原型链o上的属性
}

for(var key1 in obj) {
  if (obj.hasOwnProperty(key1)){  // 过滤o的属性
    console.log(key1);
  }
}

创建对象的方式(js中)

1.字面量的方式创建对象

2.使用new关键字创建对象(new构造器)

3.通过原型继承创建对象(Object.create(obj.prototype))

  1. 字面量的方式创建对象

    var obj = {
         name : "zhangsan",
         age : 18,
        sayHi : function () {
             console.log("你好啊!我是:" + this.name)
        }
    }
    

  2. 使用new关键字创建对象

    var obj1 = new Object();
    obj1.name = "123";           // 动态的给对象添加属性,js是一种动态的语言
    console.log(obj1.name);      // 123
    

    使用构造函数创建对象

    function Cat (name, color) {
         this.name = name;
         this.color = color;
         this.run = function() {
             console.log("跑得快");
     }
    }
    var cat = new Cat("coco","white");
    console.log(cat.name);       // coco
    console.log(cat.color);      // white
    
  1. 通过原型继承创建对象

    /**
     * 使用对象原型创建新的实例
     * @param obj 是被创建出来的对象的原型对象
     * @returns 返回对象
     */
    function createObj(obj){
        if (obj == null) throw TypeError;
        if (Object.create) {
            return Object.create(obj);
        }
        if (typeof obj !== "object" && typeof obj !== "function") throw TypeError;
        function F(){};
        F.prototype = obj;
        return new F();
    }
    
    // 创建一个空对象
    var nullObj = createObje(Object.prototype);  // 和直接量{}一样
    

对象的方法

1.公共方法

在原型上定义公共方法要比在构造函数中定义更节省硬件的损耗

// 构造函数
function User(name,age) {
    this.name = name;
    this.age = age;
    this.study = function(course){
        console.log('正在学习'+course);
    }
}

// 给构造函数的原型对象添加方法,
// 这些方法在每个由User构造器实例化出来的对象都可以访问
// 称为公共方法
User.prototype.getName = function () {
    return this.name;
}
User.prototype.getAge = function () {
    return this.age;
}

var u = new User('张三',20);
console.log(u.getName());
console.log(u.getAge());

2.私有方法

对象的私有方法只能内部调用

function Classroom(students, teacher) {
     this.students = students;
     this.teacher = teacher;

     // 私有方法,返回学生的数量
     _studentsCount = function () {
         console.log('学生总数:' + students.length);
     }
     // 对象内部调用
     _studentsCount();
}

var classOne = new Classroom(['小张','小米'],'Mr.Li');
console.log(classOne.teacher);
// classOne._studentsCount();   // 错误,因为不是对象的公共属性

3.特权方法

特权方法可以通过自身的公有方法访问到自身的私有成员。

// 特权方法:获取用户的出生年份
function User(name, age) {
    // 私有变量
    var year = (new Date()).getFullYear() - this.age;
  
    this.name = name;
    this.age = age;
    // 创建一个特权方法,能够访问私有变量year,并保证自身是公有可访问的
    this.getBornYear = function () {
        console.log(year);
    }
}

var user = new User('大熊', 1989);
user.getBornYear();

4.静态方法

静态方法通过构造函数名就可以直接调用

/**
 * 动态生成方法(get/set)
 * @param properties 是一个对象
 * @constructor
 */
function User(properties) {
    for (var item in properties) {
        (function (currentObj) {
            var p = item;
            // get 获取某个属性
            currentObj['get'+p] = function () {
                return properties[p];
            }

            // set 设置某个属性
            currentObj['set'+p] = function (val) {
                properties[p] = val;
            }
        })(this); // 将当前对象传递到闭包中
    }
}

// 创建一个新的用户实例,并把有两个属性的对象作为参数传递进入
// user对象的属性是私有的,只能通过get/set获取
var user = new User({
    name: '张三',
    age: 18,
});

// console.log(user.name);  // 错误的undefined , 会给对象添加属性
console.log(user.getname());

// 给User对象添加静态方法,可以做任意的事情
User.staticMethod = function () {
    console.log('我是静态方法')
};
// 调用静态方法
User.staticMethod();

对象的访问方式

  1. 对象的属性访问,可以通过点语法访问符合标识符命名规则的属性。如:object.property
  2. 对象的key 访问,可以访问不符合标识符的命名规则的属性。如:object['prefix-name']

标识符命名规则:只能包含字母、美元符合、数字和下划线,并且不能以数字开头

Object.defineProperty(obj, "x", { value : 1 }); 此时,enumerable默认为false

obj.x = 1;如果使用直接赋值的方式创建对象的属性,则这个属性的enumerable为true

使用构造函数创建对象的升级

  1. 构造函数里面只定义属性
  2. 方法放在构造函数的原型上定义。这样做的目的可以减少每次创建对象时需要的内存空间,只需开辟用于存放属性的空间就可以了,至于方法可以通过对象的内部原型 .__proto__ 到构造函数的原型上调用需要的方法。
// 未升级的写法
function Cat (name, color) {
    this.name = name;
    this.color = color;
    this.run = function() {
        console.log("跑得快");
    }
}

// 第一次升级的写法
function Cat (name, color) {
    this.name = name;
    this.color = color;
}

Cat.prototype = {
    // 在构造函数的原型上定义run方法,这样所有通过构造函数Cat创建出来的对象都可以使用run方法
    run = function() {
        console.log("跑得快");
    }
}


// 第二次升级
function Cat (options) {
    this.name = name;
    this.color = color;
}

Cat.prototype = {
    run = function() {
        console.log("跑得快");
    }
}

var cat = new Cat({
    name: "susu",
    color: "black"
});

// 第三次升级
function Cat (options) {
    this._init(options);
}

Cat.prototype = {
    run = function() {
        console.log("跑得快");
    },
    _init : function (options) {
        this.name = options.name || "";
        this.color = options.color || "";
    }
}

使用原型的注意事项

1.使用对象访问属性的时候,如果在本身内找不到就会去原型中查找;但是使用点语法进行属性赋值的时候,并不会去原型中进行查找;使用点语法赋值的时候,如果对象中不存在该属性,就会给该对象新增该属性,而不会去修改原型中的属性(实例只使用原型不修改原型,但原型是可以被修改的)

2.如果在原型中的属性是引用类型的属性,那么所有的对象共享该属性,并且一个对象修改了该引用类型属性中的成员,其他对象也都会受影响 (原型中的引用类型属性被修改,其他引用该属性的对象的值也跟着被改变,因为引用类型的赋值是一个地址)

3.一般情况下不会将属性放到原型对象中,一般情况下原型中只会放需要共享的方法或常量

// 构造函数
function Person(){
  
}

// 字面量对象
var x = {
    brand:"laosilaisi",
    type:"huanying"
};

// 为Person构造函数的原型添加属性,该属性是一个引用类型的
Person.prototype.car = x;

var p = new Person();
console.log(p.car.brand);   // laosilaisi

// 替换Person原型上的属性car的引用(此时的car指向另一个对象,该对象已经不是x了)
Person.prototype.car = {
    brand:"BYD"
};

var p1 =new Person();
console.log(p1.car.brand);  // BYD
console.log(p.car.brand);   // BYD  

p.car = {
    // 为p对象添加一个car属性,此时的p只会使用自身的car属性,不会再去原型对象上找car属性了
};
console.log(p.car.brand);   //  在自身找到car属性  undefied

// 结论:
// 1.通过实例是不能修改原型上的成员的,但是可以访问原型上的成员。
// 2.通过实例的构造函数的prototype属性可以修改原型。
// 3.原型上的属性是引用类型时,只要被修改,引用该属性的其他变量的值也发生变化。

对象与原型及原型链之间的关系

构造器:Foo (每一个构造器都有prototype属性)

构造器原型:Foo.prototype (构造器原型也是对象,所以也有proto属性)

对象:obj (所有的对象都有proto属性)

当定义一个函数function Foo(){} 时,系统会默认的为该函数Foo 添加一个属性prototype ,当通过函数Foo使用new关键字创建对象时obj 时,obj可以通过对象自身的属性proto 指向该对象的构造器的原型对象Foo.prototype

function Foo () {
    // 系统会默认为该函数添加prototype属性
}

Foo.prototype.z = 3;    // 原型上的属性
var obj = new Foo();    // 创建实例obj

//obj可以通过自身的proto属性指向构造器(Foo)的原型对象(Foo.prototype),从而可以访问到在原型链中的z
console.log(obj.z);     //3   

obj.z = 5;
console.log(obj.z);     //5

delete obj.z;           //true
console.log(obj.z);     //3

delete Foo.prototype.z; //true
console.log(obj.z);     //undefined

实例截图

1

object.png

2


creat.png

深拷贝/浅拷贝

浅拷贝:只拷贝当前对象中的所有属性(即只复制一层对象的属性)

深拷贝:而深复制则递归复制了对象所有层级的属性,包括属性中的引用类型

/**
 * 浅拷贝
 * @param src
 * @returns {{}}
 */
function shallowCopy(obj) {
    var newObj = {};
    for (var prop in obj) {
        if (obj.hasOwnProperty(prop)) {
            newObj[prop] = obj[prop];
        }
    }
    return newObj;
}

/**
 * 深拷贝
 * @param obj
 */
var deepCopy = function(obj) {
    var str; // 通过序列化与反序列化也可以创建对象,所有定义了该变量
    var newObj = obj.constructor === Object ? {} : [];
    if (typeof obj !== 'object'){
        return;
    }else if(window.JSON){
        str = JSON.stringify(obj);  // 序列化对象
        newObj = JSON.parse(str);   // 还原
    }else{
        for (var key in obj) {
            newObj[key] = typeof obj[key] === 'object' ? deepCopy(obj[key]) : obj[key];
        }
    }
    return newObj;
}



var obj = { a:1, arr: [2,3] };

// 浅复制会导致 obj.arr 和 shallowObj.arr 指向同一块内存地址,
// 任何一方被修改,另一方也被改变
var shallowObj = shallowCopy(obj);
shallowObj.arr.push('浅拷贝对象会改变原对象的引用类型属性的值');
console.log(obj);
console.log(shallowObj);

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

推荐阅读更多精彩内容

  • 对象: 1. 对象中包含一系列的属性,这些属性是无序的。 2. 每个属性都有一个字符串key和对应的value;(...
    一树青枫阅读 340评论 0 1
  •   面向对象(Object-Oriented,OO)的语言有一个标志,那就是它们都有类的概念,而通过类可以创建任意...
    霜天晓阅读 2,092评论 0 6
  • 文章如有错误请联系我 anmingzhe@me.com Object.assign() 合并对象 多个参数,除第一...
    安明哲阅读 1,443评论 0 0
  • 内容来自《JavaScript高级程序设计》第三版第6章第3节 原型链 ECMAScript中描述了 原型链的概念...
    angelwgh阅读 233评论 0 0
  • 第3章 基本概念 3.1 语法 3.2 关键字和保留字 3.3 变量 3.4 数据类型 5种简单数据类型:Unde...
    RickCole阅读 5,096评论 0 21