JavaScript对象、函数(你不知道的JavaScript)

本文深入探讨JavaScript对象的创建、属性、原型链,及函数的特性与构造函数的正确使用,揭示对象与函数的底层机制。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

JavaScript对象、函数(你不知道的JavaScript)

一、对象

 
  1. var obj = {};

  2. obj[true] = "foo";

  3. obj[3] = "bar";

  4. obj[obj] = "baz";

  5.  
  6. obj["true"];

  7. obj["3"];

  8. obj["[object Object]"];

二、数组也是对象

 
  1. var ary = ["foo", 42, "bar"];

  2. ary.baz = "baz";

  3. ary.length; //3

  4. ary.baz;

  5.  
  6. /* 如果试图想数组添加一个属性,但是属性名“看起来”像一个数字,那么它会变成一个数值下标 */

  7. ary["4"] = "baz";

  8. ary.length; //5

  9. ary[4];

三、对象属性

writable:可写(修改)
enumerable:可枚举(for ... in)
configurable:可配置(配置、删除)
注意:delete只能删除对象(可删除)属性,即configurable为true的属性。

 
  1. var obj = {};

  2. Object.defineProperty(obj, "a", {enumerable:true, value:2});

  3. Object.defineProperty(obj, "b", {enumerable:false, value:3});

  4.  
  5. /* 检查给定属性名是否直接存在于对象中(而不是在原型链上)并满足enumerable:true */

  6. obj.propertyIsEnumerable("a"); //true

  7. obj.propertyIsEnumerable("b"); //false

  8.  
  9. /* 返回一个数组,包含所有(自身)可枚举属性 */

  10. Object.keys(obj); //["a"]

  11. /* 返回一个数组,包含所有(自身)属性 */

  12. Object.getOwnPropertyNames(obj); //["a", "b"]

注意:in和hasOwnProperty(..)的区别在于是否查找[[Prototype]]链,然而,Object.keys(..)和Object.getOwnPropertyNames(..)都只会查找对象直接包含的属性。

四、对象原型

获取对象原型:Object.getPropertyOf(..)

 
  1. function Foo(){}

  2. var foo = new Foo();

  3. Foo.prototype === Object.getPrototypeOf(foo); //true

注意:在面向类的语言中,类可以复制(实例化)多次,就像用模具制作东西一样。在JavaScript中,并没有类似的复制机制。不能创建一个类的多个实例,只能创建多个对象,它们[[property]]关联的是同一个对象。这样就可用通过委托访问对象的属性和方法了。
类理论:在构造完成后,通常只需要操作这些实例(而不是类),因为每个实例都有你需要完成任务的所有行为。

五、原型链[[prototype]]

[[prototype]]机制就是存在于对象中的一个内部链接,它会引用其他对象。

 
  1. var foo = {

  2. something:function(){

  3. // ....

  4. }

  5. };

  6. var bar = Object.create(foo); // 创建新对象bar,并将其关联到对象foo上。

Object.create(null)会创建一个拥有空链接的对象,这个对象无法进行委托,其不存在原型链,所以instanceof总是返回false。其不受原型链干扰,非常适合用来存储数据!
对象之间的关系不是复制而是委托!!!
谈及原型链不得不提及我们经常在JavaScript中的类型检查!即内省:检查实例的类型;主要目的是通过创建方式来判断对象的结构和功能。
方式一:a instanceof Foo
方式二:“鸭子类型”:如果看起来像鸭子,叫起来像鸭子,那就一定是鸭子。

 
  1. if(a.something) {

  2. a.something(); // something()并不一定在a自身上

  3. }

方式三:isPrototypeOf()、getPrototypeOf()
建议使用方式三!!!

六、函数

JavaScript中的函数无法(用标准、可靠的方法)真正地复制,所以只能共享函数对象的引用。这意味着,如果修改共享函数,比如增加一个属性,所有引用地方都会随之修改!

七、构造函数

 
  1. function Foo(){}

  2. Foo.prototype = {};

  3.  
  4. var a1 = new Foo();

  5. a1.constructor === Foo; //false

  6. a1.constructor === Object; //true

详解:
创建一个新对象并替换了函数默认的.prototype对象引用,那么新对象并不会自动获得.constructor属性。
a1并没有.constructor属性,所以它会委托[[Prototype]]链上的Foo.prototype。但是这个对象也没有.constructor属性,所以它会继续委托给链顶端的Object.protoype。
实际上,对象的.constructor会默认指向一个函数,这个函数可以通过对象的.prototype引用!
总之,constructor并不表示被构造!!!改变prototype并不能彻底改变继承关系!!!

 
  1. function Foo(name){

  2. this.name = "FenFei"

  3. }

  4. function Bar(){}

错误理解一:

 
  1. Bar.prototype = Foo.prototype;

  2. Bar.prototype.myTest = 123;

  3. new Foo().myTest; //123

并不会创建一个关联到Bar.prototype的新对象,它只是让Bar.prototype直接引用Foo.prototype对象。当执行类似Bar.prototype.myTest = 123的赋值语句时会直接修改Foo.prototype对象本身。

错误理解二:

 
  1. Bar.prototype = new Foo();

  2. new Bar("Camile").name; //FenFei

的确会创建一个关联到Bar.prototype的新对象。但是它使用了Foo()的“构造函数调用”,如果Foo有副作用(比如注册到其他对象、给this添加数据属性等等),就会影响Bar()的后代。

正确处理方式一[ES6之前]:

 
  1. Bar.prototype = Object.create(Foo.prototype);

  2. Bar.prototype.myTest = 123;

  3. new Foo().myTest; //undefined

  4. new Bar("Camile").name; //undefined

正确处理方式二[ES6之后]:

 
  1. Object.setPrototypeOf(Bar.prototype, Foo.prototype);

  2. Bar.prototype.myTest = 123;

  3. new Foo().myTest; //undefined

  4. new Bar("Camile").name; //undefined

八、函数关系

(1)在a的整条[[prototype]]链中是否有指向Foo.prototype的对象?

a instanceof Foo;

(2)在a的整条[[prototype]]链中是否出现过Foo.prototype?

Foo.prototype.isPrototypeOf(a);	// 不需要间接引用函数Foo,它的prototype属性会自动访问。

(3)区别
isPrototypeOf()方法可以判断对象间的关系。
b是否出现在c的[[prototype]]链中?

b.isPrototypeOf(c);

示例:

 
  1. function Foo(){}

  2. var a = new Foo();

  3. console.log(a instanceof Foo); // true

  4. console.log(Foo.prototype.isPrototypeOf(a)); // true

  5. var b = {};

  6. var c = Object.create(b);

  7. console.log(b.isPrototypeOf(c)); // true

  8. console.log(b instanceof c); // TypeError

在传统面向类的语言中,类定义之后就不会进行修改,所以类的设计模式不支持修改。但是JavaScript最强大的特性之一就是它的动态性,任何对象的定义都可以修改(除非你把它设置成不可变)!

[转载请标明出处:http://blog.youkuaiyun.com/ligang2585116]

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值