JSCORE--对象

用构造函数创建对象

通过函数来创建对象: 因为函数内部的执行流程是固定的, 所以同一个函数生成的多个对象 一定是功能相同的

相当于流水线作业: 在保障质量的同时 保障合格率

new运算符: 一种辅助的手段

  • 可以从代码的角度 简化构造函数的写法: 省略一些固定的代码 
function 构造函数(参数) {
    this.属性= 参数;
}
var 对象 = new 构造函数(参数);


function emp(name,age){
    this.uname = name
    this.uage = age
    this.run = function(){
        console.log('go')
    }
}
var emp1 = new emp('taeyeon',18)
console.log(emp1)

对象创建的过程

通过构造函数创建对象时一共做了以下几件事:

1. 创建一个新的空对象
2. 继承构造函数的原型对象
2.1 用新对象调用构造函数
2.2 先将构造函数内所有this指向新对象
3. 给新对象添加新属性和新方法
4. 返回新对象的地址
function Student(name, age) {
    this.sname = name;
    this.sage = age;
    this.intr = function () {
        console.log(this.sname + this.sage);
    }
}
// 1.创建一个新的空对象{}
// 2.新对象{}继承Student的原型对象Student.prototype,新对象{}.__proto__==Student.prototype
// 3.{}调用构造函数Student.prototype.constructor
// 3.1 构造函数被调用,创建Student函数作用域
// 3.2 Student函数作用域中的this指向创建的新对象{}
// 3.3 给this对象,也就是新对象{}添加属性(sname,sage,intr)
// 3.4 Student函数作用域中创建局部变量(name, age),接受参数('伍六七', 18)
// 3.5 Student函数作用域中的(name, age),赋值给this对象,也就是新对象{}的属性(sname,sage)
// 3.6 创建匿名函数对象function () { console.log(this.sname + this.sage); },赋值给this.intr
// 4. 返回新对象{}的地址值给aq
var aq = new Student('伍六七', 18);
console.log(aq);

 原型对象和继承

原型对象:在函数创建时会同时创建一个对象,该对象是所有通过构造函数创建对象的父对象,包含了所有子对象共享的属性和方法

继承:子类具有父类的属性和方法,正常来讲类是抽象的,而对象是类的实例化,但是在js当中类也是一个对象

每个对象拥有一个原型对象,对象以其原型为模板,从原型继承方法和属性

原型对象可以通过构造函数.prototype和通过构造函数创建对象的.__proto__访问

共有属性:保存在原型对象中,所有子对象共有的属性

自有属性:保存在子对象中,子对象自己所有的属性

// 构造函数
function Student(sname) {
    this.sname = sname;
}
// 构造函数.prototype属性为原型对象
// 向Student的原型对象中添加共有方法intr()
Student.prototype.intr = function () {
    console.log(`I'm ${this.sname}`)
}
// 向Student的原型对象中添加共有属性sage
Student.prototype.sage = 18;
var aq = new Student("伍六七");
var ss = new Student("梅十三");
console.log(aq);
console.log(ss);
// ap和ss的intr方法均为Student的原型对象中的intr
aq.intr();
ss.intr();
// aq和ss的父类是同一个对象
console.log(aq.__proto__ == ss.__proto__);//true
// aq的父类和Student的原型对象是一个
console.log(aq.__proto__ == Student.prototype)//true

原型链

原型对象也可能拥有原型,并从中继承方法和属性,一层一层、以此类推,这种关系常被称为原型链 (prototype chain)

每个实例对象(object)都有一个私有属性(称之为 proto)指向它的构造函数的原型对象(prototype),该原型对象也有一个自己的原型对象(proto) ,层层向上直到一个对象的原型对象为 null,根据定义,null 没有原型,并作为这个原型链中的最后一个环节

// Object是String对象和Number对象的原型对象
Object.prototype.intr = function(){
    console.log('Object->intr');
}
var str = new String();
str.intr();
console.log(str);
// String构造函数的原型对象是String对象
console.log(str.__proto__);
// Object
console.log(str.__proto__.__proto__);
//null
console.log(str.__proto__.__proto__.__proto__);

var num = new Number();
num.intr();
console.log(num);
// Number构造函数的原型对象是Number对象
console.log(num.__proto__);
// Object
console.log(num.__proto__.__proto__);
// true
console.log(str.__proto__.__proto__ == num.__proto__.__proto__);

多态

js中的多态:一个属性或方法,不同的情况下表现出不同的状态

重写(override):当子类中的属性和方法与父类重名时,使用子对象自己的同名属性

多态是概念,重写是实现方法及效果

    function Empolyee(ename, eage) {
      this.ename = ename
      this.eage = eage
    }
    Empolyee.prototype.role = '崔三娘'
    var emp1 = new Empolyee('Taeyeon', 19);
    var emp2 = new Empolyee('Yoona', 18)
    console.log(emp1.role, emp2.role);
    // 重写
    emp2.role = '宁红夜'
    console.log(emp1.role, emp2.role);

数据描述符

数据描述符是一个具有可写或不可写值的属性。

每个数据属性其实都是一个对象,每个属性对象中都包含一个value属性和三个权限属性。

  • value 属性值
  • writablefalse, 控制是否可以修改
  • enumerablefalse, 控制是否可被for...in遍历
  • configurablefalse 控制是否能删除属性,是否可修改writableenumerable属性

writable , enumerable , configurable 默认值均为true

enumerable设置为false后依然可以通过 对象.属性名 访问

configurable一旦设置为false则不可逆

    //使用获取数据属性对象Object.getOwnPropertyDescriptor(对象名, "属性名")
    var user = {
      uid: 1001,
      uname: '崔三娘'
    }
    for (var key in user) {
      console.log(user[key]);
    }
    // 获取uid数据属性对象
    var obj = Object.getOwnPropertyDescriptor(user, 'uid')
    console.log(obj);

    // 修改属性对象Object.defineProperty.(对象名, 属性名, {属性对象的属性})
    // 设置user的uid属性只读
    Object.defineProperty(user, 'uid', {
      writable: false, // 控制是否可以修改
      enumerable: false, // 控制是否可以被for...in遍历
      configurable: false // 控制是否能删除属性,是否可修改前两个属性
    })

    user.uid = 1222
    console.log(user); // uid未被修改

    // 只能输出第一个属性
    for (var key in user) {
      console.log(user[key]);
    }

    // 删除uid属性
    delete user.uid
    console.log(user); // 删除失败

    // configurable: false 控制writable和enumerable属性适合可以修改,一旦设置不可逆
    Object.defineProperty(user, 'uid', {
      writable: true, // 报错
      enumerable: true // 报错
    })

同时设置多个属性对象

    // "use strict"
    var user = {
      uid: 1001,
      uname: "崔三娘"
    }
    Object.defineProperties(user, {
      uid: {
        writable: false,
      },
      uname: {
        writable: false,
      },
      // 语法糖: value: function(){}
      intro: {
        value() {
          console.log(this.uid, this.uname)
        },
      },
    });
    user.intro();
    user.uid = 1222; // 修改失败,在严格模式下报错
    user.uname = '宁红夜'; // 修改失败,在严格模式下报错
    console.log(user);

把设置放在对象的构造函数里,所有创建的对象都会被保护

    function User(uid, uname) {
      // this.uid = uid
      Object.defineProperties(this, {
        uid: {
          value: uid,
          writable: false,
          enumerable: true,
          configurable: false
        },
        // 如果对象中没有要设置的属性,会自动添加该属性,但是须要写完value+三个属性
        // 通过defineProperty定义的新属性的writable和enumerable默认值是false
        uname: {
          value: uname,
          configurable: false
        }
      })
    }
    var nhy = new User(1003, '宁红夜')
    nhy.uid = 1 // 修改失败
    console.log(nhy);
    // 由于uname是通过defineProperty定义的新属性的,enumerable默认值是false,无法遍历
    for (var key in nhy) {
      console.log(nhy[key]);
    }

访问器描述符

访问器描述符是由 getter/setter 函数对描述的属性。描述符只能是这两种类型之一,不能同时为两者。

访问器属性:每个访问器属性其实都是一个对象,每个属性对象中都包含gettersetter函数和二个权限属性

访问器属性不能直接定义,必须使用Object.defineProperty()

在访问属性值的会自动执行gettersetter函数,可以对属性值做一些中间处理

    function User(uid, uname) {
      this.uid = uid
      this.uname = uname
    }
    var csn = new User(1002, '崔三娘')
    Object.defineProperties(csn, {
      // 定义一个新的访问器属性uage
      uage: {
        // 访问uage属性时会自动调用get方法
        get: function () {
          console.log('自动调用了uage的get()');
          return this._uage
        },
        // 修改uage属性时会自动调用set方法
        // set: function (value) {} 语法糖 -> set(value) {}
        set(value) {
          // value会自动接到要赋值的新值
          console.log(`自动调用了uage的set(${value})`);
          if (value >= 0 && value <= 3) {
            this._uage = value
          } else {
            throw Error('年龄必须介于0~3之间')
          }
        },
        enumerable: true,
        configurable: false,
      },
      // 实际被访问的属性,当访问和修改uage时,实际是在对_uage.value操作
      _uage: {
        value: csn.uage,
        writable: true,
        enumerable: false,
        configurable: false
      }
    })

    // 访问uage属性值:都会自动调用get()方法
    csn.uage
    console.log(csn);
    // 尝试修改uage的属性值为正确的值
    csn.uage = 2
    // _属性依旧可以通过 对象.属性名 来访问
    console.log(csn._uage); //2
    csn._uage = 20
    console.log(csn); // 不报错
    // 修改uage的属性值为错误的值
    csn.uage = -2
    console.log(csn); // 报错

防扩展

对象的extensible属性用于表示是否允许在对象中动态添加新的属性

Object.isExtensible() 方法判断一个对象是否是可扩展的(是否可以在它上面添加新的属性)

Object.preventExtensions()方法让一个对象变的不可扩展,也就是永远不能再添加新的属性,不可扩展对象的原型是不可变的

    // "use strict"
    var user = {
      uid: 1001,
      uname: "胡桃"
    }
    // 新对象默认是可扩展的

    let a = Object.isExtensible(user); // true
    console.log(a);
    // 变为不可扩展
    Object.preventExtensions(user);
    let b = Object.isExtensible(user); // false
    console.log(b);

    // 添加属性失败
    user.uage = 18;
    console.log(user);
    // 依然可以删除属性,只是不能添加
    delete user.uid;
    console.log(user);
    // 报错
    // user.__proto__ = { obj: "obj" };
    console.log(user);

密封

Object.seal()方法封闭一个对象,出了能修改现有属性的属性值,其他都不能修改。

Object.seal(obj)Object.seal()自动将对象的extensible:false ,configurable:false

    var obj = {
      prop: function () { },
      foo: 'bar'
    }
    // 添加新属性
    // 可以更改或删除现有的属性
    obj.foo = 'baz';
    obj.lumpy = 'woof';
    delete obj.prop
    console.log(obj);

    var o = Object.seal(obj);
    console.log(o === obj); //true
    let a = Object.isSealed(obj)
    console.log(a); // true

    // 仍然可以修改密封对象的属性值
    obj.foo = 'asd'
    console.log(obj);

    // 但是你不能将属性重新定义成为访问器属性
    // Uncaught TypeError: Cannot redefine property: foo
    Object.defineProperty(obj, 'foo', {
      get() { return 'g' }
    }) // throws a TypeError

    // 除了属性值以外的任何变化,都会失败.
    // 严格模式下会报错,非严格模式会静默失败
    // 添加属性将会失败
    obj.quaxxor = 'ttht'
    // 严格模式下会报错,非严格模式会静默失败
    // 删除属性将会失败
    delete obj.foo
    console.log(obj);

    // 在严格模式下,这样的尝试将会抛出错误
    function fail() {
      'use strict';
      delete obj.foo;
      obj.sparky = 'arf'
    }
    fail()

    // 通过Object.defineProperty添加属性将会报错
    Object.defineProperty(obj, 'ohai', {
      value: 17
    }); // throws a TypeError
    Object.defineProperty(obj, 'foo', {
      value: 'eit'
    }); // 通过Object.defineProperty修改属性值

冻结

Object.freeze() 方法可以冻结一个对象,一个被冻结的对象再也不能被修改。

自动将对象的extensible : falseconfigurable : falsewritable : false

冻结对象

var obj = {
  prop: function() {},
  foo: 'bar'
};

// 新的属性会被添加, 已存在的属性可能
// 会被修改或移除
obj.foo = 'baz';
obj.lumpy = 'woof';
delete obj.prop;

// 作为参数传递的对象与返回的对象都被冻结
// 所以不必保存返回的对象(因为两个对象全等)
var o = Object.freeze(obj);

o === obj; // true
Object.isFrozen(obj); // === true

// 现在任何改变都会失效
obj.foo = 'quux'; // 静默地不做任何事
// 静默地不添加此属性
obj.quaxxor = 'the friendly duck';

// 在严格模式,如此行为将抛出 TypeErrors
function fail(){
  'use strict';
  obj.foo = 'sparky'; // throws a TypeError
  delete obj.quaxxor; // 返回true,因为quaxxor属性从来未被添加
  obj.sparky = 'arf'; // throws a TypeError
}

fail();

// 试图通过 Object.defineProperty 更改属性
// 下面两个语句都会抛出 TypeError.
Object.defineProperty(obj, 'ohai', { value: 17 });
Object.defineProperty(obj, 'foo', { value: 'eit' });

// 也不能更改原型
// 下面两个语句都会抛出 TypeError.
Object.setPrototypeOf(obj, { x: 20 })
obj.__proto__ = { x: 20 }

冻结数组

let a = [0];
Object.freeze(a); // 现在数组不能被修改了.

a[0]=1; // fails silently
a.push(2); // fails silently

// In strict mode such attempts will throw TypeErrors
function fail() {
  "use strict"
  a[0] = 1;
  a.push(2);
}

fail();

被冻结的对象是不可变的。但也不总是这样。下例展示了冻结对象不是常量对象(浅冻结)。

obj1 = {
  internal: {}
};

Object.freeze(obj1);
obj1.internal.a = 'aValue';

obj1.internal.a; // 'aValue'

对于一个常量对象,整个引用图(直接和间接引用其他对象)只能引用不可变的冻结对象。冻结的对象被认为是不可变的,因为整个对象中的整个对象状态(对其他对象的值和引用)是固定的。注意,字符串,数字和布尔总是不可变的,而函数和数组是对象。

要使对象不可变,需要递归冻结每个类型为对象的属性(深冻结)。当你知道对象在引用图中不包含任何 '环' (循环引用)时,将根据你的设计逐个使用该模式,否则将触发无限循环。对 deepFreeze() 的增强将是具有接收路径(例如Array)参数的内部函数,以便当对象进入不变时,可以递归地调用 deepFreeze() 。你仍然有冻结不应冻结的对象的风险,例如[window]

// 深冻结函数.
function deepFreeze(obj) {
  // 取回定义在obj上的属性名
  var propNames = Object.getOwnPropertyNames(obj);
  // 在冻结自身之前冻结属性
  propNames.forEach(function(name) {
    var prop = obj[name];
    // 如果prop是个对象,冻结它
    if (typeof prop == 'object' && prop !== null)
      deepFreeze(prop);
  });
  // 冻结自身(no-op if already frozen)
  return Object.freeze(obj);
}

obj2 = {
  internal: {}
};

deepFreeze(obj2);
obj2.internal.a = 'anotherValue';
obj2.internal.a; // undefined

对比Object.seal()

Object.seal()密封的对象可以改变它们现有的属性。使用Object.freeze() 冻结的对象中现有属性是不可变的。

创建新对象

Object.create()方法创建一个新对象,使用现有的对象来提供新创建的对象的proto

Object.create(proto,[propertiesObject])

var A = {
    x : 0,
    y : 0
}
var a = Object.create(A);
console.log(a); // {}
console.log(a.__proto__); // {x:0,y:0}

 使用 Object.create 的 propertyObject参数

   var o;
    // 创建一个原型为null的空对象
    o = Object.create(null)

    o = {}
    // 以字面量方式创建的空对象就相当于:
    o = Object.create(Object.prototype)

    console.log(o);

    o = Object.create(Object.prototype, {
      // foo会成为所创建对象的数据属性
      foo: {
        writable: true,
        configurable: true,
        value: "hello"
      },
      // bar会成为所创建对象的访问器属性
      bar: {
        configurable: false,
        get: function () { return 10 },
        set: function (value) {
          console.log("Setting **o.bar** to", value);
        }
      }
    });
    console.log(o);

    function Constructor() { }

    o = new Constructor();
    // 上面的一句就相当于:
    o = Object.create(Constructor.prototype);
    // 当然,如果在Constructor函数中有一些初始化代码,Object.create不能执行那些代码
    // 创建一个以另一个空对象为原型,且拥有一个属性p的对象
    o = Object.create({}, { p: { value: 42 } })
    console.log(o);
    // 省略了的属性特性默认为false,所以属性p是不可写,不可枚举,不可配置的:
    o.p = 24
    console.log(o.p);// 42

    o.q = 12
    console.log(o); //{q: 12, p: 42}
    for (var prop in o) {
      console.log(prop);
    }
    //'q'
    let a1 = delete o.p
    console.log(a1); // false

    //创建一个可写的,可枚举的,可配置的属性p
    o2 = Object.create({}, {
      p: {
        value: 42,
        writable: true,
        enumerable: true,
        configurable: true
      }
    });

this的对象指向

当前调用函数时的对象

全局中的this

// 在浏览器中全局中的this和全局函数中的this全都是指向Window
console.log(this);
function fun() {
    // 在浏览器中this和全局函数中的this全都是指向Window
    console.log(this)
}
fun();

对象内

对象内this指向掌握一个原则,谁调用就指向谁

// obj.fun()  this->obj
var obj = {
    name: 'zs',
    fun: function () {
        console.log(this);
    }
};
obj.fun();

// new Fun()  this->new新对象
function Student(sname,sage){
    this.name = 'zs',
    this.fun = function(){
        console.log(this);
    }
}
new Student().fun();

// 构造函数.prototype.fun=function(){this->?}  this->子对象.fun()中的子对象
String.prototype.intr = function(){
    console.log(this);
}
var str = new String('str');
str.intr();

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值