javascript对象(上篇)

var a = 1;

console.log(typeof a);// 'number'

var b = '1';

console.log(typeof b);// 'string'

var c = true;

console.log(typeof c);// 'boolean'

var d = null;

console.log(typeof d);// 'object'

var e = undefined;

console.log(typeof e);// 'undefined'

函数对象

typeof是用来判断变量类型

instancaof是用来检测这个实例是不是由这个类所创建,换言之,就是检测这个实例对象是不是这个类new出来的。

constructor是每一个实例对象都拥有的属性,而这个属性也相当于是一个指针,它指向于创建当前对象的对象。

从下面的代码可以看出,在javascript中函数即是对象。

var o = new Object();

console.log(typeof o);// 'object'

console.log(o instanceof Object);// true

console.log(o.constructor === Object);// true

console.log(o instanceof Function);// false

console.log(o.constructor === Function);// false

var f = new Function();

console.log(typeof f);// 'function'

console.log(f instanceof Function);// true

console.log(f.constructor === Function);// true

console.log(f instanceof Object);// true

console.log(f.constructor === Object);// false

console.log(Function instanceof Object);// true

console.log(Function.constructor === Object);// false

console.log(Function instanceof Function);// true

console.log(Function.constructor === Function);// true

javascript对象可以任意自定义属性和方法,没有c++和java中的class的约束。

var o = {

name:'nexus', // 定义一个属性

showMsg:function(){ // 定义一个方法

console.log('my name is ' + this.name);

}

};

console.log(o.name);// 'nexus'

o.showMsg();// 'my name is nexus'

例子中新建了一个对象o,其中o对象有属性name和方法showMsg。

o.name就是'neuxs'。

o.showMsg()就是运行o的showMsg函数,其中调用showMsg函数的宿主是o,所以此时的this是o,this.name也就是'nexus',打印'my name is nexus'。

构造函数

例子中定义了一个函数,返回一个对象,其中包括属性和方法,属性name和age通过行参自定义。

function Person(name,age){

var o = new Object();

o.name = name;

o.age = age;

o.showMsg = function(){

console.log(this.name + ':' + this.age);

};

return o;

}

var p = Person('nexus',18);

console.log(p.name);// 'nexus'

console.log(p.age);// 18

p.showMsg();// 'nexus : 18'

函数还可以作为构造函数使用,像上面所述的 Object 和 Array 原生构造函数一样,在运行时会自动出现在执行环境中:

function Person(name,age){

this.name = name;

this.age = age;

this.showMsg = function(){

console.log(this.name + ':' + this.age);

};

}

var p = new Person('nexus',18);

console.log(p.name);// 'nexus'

console.log(p.age);// 18

console.log(p.showMsg());// 'nexus : 18'

要创建 Person 的新实例,必须使用 new 操作符。

创建一个新对象,

将构造函数的作用域赋给新对象(因此 this 就指向了这个新对象),

执行构造函数中的代码(为这个新对象添加属性和方法),

返回新对象。

// 作为普通函数调用,添加到 window

Person('nexus',18);

console.log(window.name);// 'nexus'

console.log(window.age);// 18

window.showMsg();// 'nexus : 18'

// 当作构造函数使用,新建一个对象p

var p = new Person('nexus', 18);

console.log(p.name);// 'nexus'

console.log(p.age);// 18

p.showMsg();// 'nexus : 18'

// 在另一个对象的作用域中调用

var o = {};

Person.call(o,'nexus',18);

console.log(o.name);// 'nexus'

console.log(o.age);// 18

o.showMsg(); // 'nexus : 18'

构造函数虽然简洁明了,但是存在缺点:

var p1 = new Person('nexus',18);

var p2 = new Person('nexus',18);

console.log(p1.name == p2.name);// true

console.log(p1.age == p2.age);// true

console.log(p1.showMsg == p2.showMsg);// false

我们可以发现,当每次建立一个新对象的时候,每个 Person 实例都包含一个不同的 showMsg() 方法,即使它们的内容相同,也需要另外开辟内存来保存。

创建两个完成同样任务的 showMsg 方法没有必要,这个时候就需要使用prototype。

prototype 原型

我们创建的每个函数都有一个 prototype(原型)属性。使用原型的好处是可以让所有对象实例共享它所包含的属性和方法。换句话说,不必在构造函数中定义对象实例的信息,而是可以将这些信息直接添加到原型中。

function Person(){}

Person.prototype.name = 'nexus';

Person.prototype.age = 18;

Person.prototype.showMsg = function(){

console.log(this.name + ':' + this.age);

};

var p1 = new Person();

p1.showMsg();// 'neuxs : 18'

var p2 = new Person();

p2.showMsg();// 'neuxs : 18'

console.log(p1.showMsg == p2.showMsg);// true

showMsg() 方法和所有属性直接添加到了 Person 的 prototype 属性中,构造函数变成了空函数。即使如此,也仍然可以通过调用构造函数来创建新对象,而且新对象还会具有相同的属性和方法。但与前面的例子不同的是,新对象的这些属性和方法是由所有实例共享的。换句话说,p1 和 p2 访问的都是同一组属性和同一个 showMsg() 函数。

function Person(){}

Person.prototype.name = 'nexus';

Person.prototype.age = 18;

Person.prototype.showMsg = function(){

console.log(this.name + ':' + this.age);

};

var p1 = new Person();

console.log(p1.name);// 'nexus',来自原型

p1.name = "noa";

console.log(p1.name);// 'noa',来自实例

delete p1.name;

console.log(p1.name);// 'nexus',来自原型

delete p1.name;

console.log(p1.name);// 'nexus',来自原型,delete不能删除原型属性

当访问 p1.name 时,需要读取它的值,因此就会在这个实例上搜索一个名为 name 的属性。这个属性确实存在,于是就返回它的值而不必再搜索原型了

使用 delete 操作符删除了 p1.name,之前它保存的 "noa" 值屏蔽了同名的原型属性。把它删除以后,就恢复了对原型中 name 属性的连接。

从代码中看出 delete 操作符只能删除对象的实例name属性,无法删除原型的 name 属性。

function Person(){}

Person.prototype.name = 'nexus';

Person.prototype.age = 18;

Person.prototype.showMsg = function(){

console.log(this.name + ':' + this.age);

};

var p = new Person();

console.log(p instanceof Object);      // true

console.log(p.constructor === Object);  // false

console.log(p instanceof Person);      // true

console.log(p.constructor === Person);  // true

重写整个原型对象 Person.prototype

function Person(){}

Person.prototype = {

name : 'nexus',

age : 18,

showMsg : function () {

console.log(this.name + ':' + this.age);

}

};

Person.prototype.name = 'nexus';

Person.prototype.age = 18;

Person.prototype.showMsg = function(){

console.log(this.name + ':' + this.age);

};

var p = new Person();

console.log(p instanceof Object);      // true

console.log(p.constructor === Object);  // true

console.log(p instanceof Person);      // true

console.log(p.constructor === Person);  // false

Person.prototype 重新设置为一个新对象。实例 p.constructor 属性不再指向 Person 了,而是指向 Object 构造函数。

如果 constructor 的值真的很重要,可以像下面设置。

function Person(){}

Person.prototype = {

constructor : Person,

name : 'nexus',

age : 18,

showMsg : function () {

console.log(this.name + ':' + this.age);

}

};

Person.prototype.name = 'nexus';

Person.prototype.age = 18;

Person.prototype.showMsg = function(){

console.log(this.name + ':' + this.age);

};

var p = new Person();

console.log(p instanceof Object);      // true

console.log(p.constructor === Object);  // false

console.log(p instanceof Person);      // true

console.log(p.constructor === Person);  // true

动态性原型

我们对原型对象所做的任何修改都能够立即反映出来:

function Person(){}

var p = new Person();

Person.prototype.sayHi = function(){

console.log('hi');

};

p.sayHi();  // 'hi'

调用 p.sayHi() 时,首先会在实例中搜索名为 sayHi 的属性,在没找到的情况下,会继续搜索原型。因为实例与原型之间的连接只不过是一个指针,而非一个副本

如果是重写整个原型对象,那么情况就不一样了。

通过 p.name 没有发生改变可以得出结论:调用构造函数时会为实例添加一个指向最初原型的 Prototype 指针,而把原型修改为另外一个对象就等于切断了构造函数与最初原型之间的联系。

function Person(){}

var p = new Person();

Person.prototype.name = 'nexus';

Person.prototype = {

constructor: Person,

name:'noa',

sayHi : function () {

console.log('hi');

}

};

console.log(p.name);// 'nexus'

p.sayName();// Uncaught TypeError: p.sayName is not a function

原生对象的原型

我们同样可以给原生对象添加方法,这里展示一下如何实现一个Array.prototype.map函数的原型方法。

if (!Array.prototype.map) {

Array.prototype.map = function (callback, thisArg) {

// 调用宿主是 null 或者 undefined,则抛出 TypeError 异常

if (this == null) {

throw new TypeError('Array.prototype.map called on null or undefined');

}

// 如果 callback 不是函数,则抛出 TypeError 异常

if (typeof callback !== 'function') {

throw new TypeError(callback + ' is not a function');

}

// 将 O 赋值为调用 map 方法的数组

var O = Object(this);

// 将 len 赋值为数组 O 的长度

var len = O.length >>> 0;

// 如果参数 thisArg 有值,则将 T 赋值为 thisArg ,否则T为 undefined

var T;

if (thisArg) {

T = thisArg;

}

// 创建新数组 A,长度为原数组 O 长度 len

var A = new Array(len);

// 将 k 赋值为0,遍历开头

var k = 0;

// 当 k < len 时,执行循环

while (k < len) {

var kValue, mappedValue;

// 遍历 O,k 为原数组索引

// 那些从来没被赋过值或者使用 delete 删除的索引则不会被调用。

if (k in O) {

// kValue 为索引 k 对应的值

kValue = O[k];

// 执行callback,this 指向 T,参数有三个,分别是 kValue:值,k :索引,O :原数组

mappedValue = callback.call(T, kValue, k, O);

// 返回值添加到新数组A中.

A[k] = mappedValue;

}

// k自增1

k++;

}

// 返回新数组A

return A;

};

}

构造函数和原型结合

构造函数用于定义实例属性,而原型用于定义方法和共享的属性。

function Person(name, age){

this.name = name;

this.age = age;

this.friends = ['a', 'b'];

}

Person.prototype = {

constructor : Person,

sayName : function(){

console.log(this.name);

}

}

var p1 = new Person('nexus', 18);

var p2 = new Person('noa', 19);

p1.friends.push('c');

console.log(p1.friends);// ['a','b','c']

console.log(p2.friends);// ['a','b']

console.log(p1.friends === p2.friends);// false

console.log(p1.sayName === p2.sayName);// true

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

推荐阅读更多精彩内容

  • ##**理解对象**## --- ###**属性类型** > JavaScript中有两种属性类型 分别是 数据属...
    nullunde阅读 277评论 0 0
  • 第5章 引用类型(返回首页) 本章内容 使用对象 创建并操作数组 理解基本的JavaScript类型 使用基本类型...
    大学一百阅读 3,151评论 0 4
  • # javascript 笔记 ## 1 - confign (sudo) - node - npm init -...
    wwzwwz阅读 166评论 0 0
  • 工厂模式类似于现实生活中的工厂可以产生大量相似的商品,去做同样的事情,实现同样的效果;这时候需要使用工厂模式。简单...
    舟渔行舟阅读 7,621评论 2 17
  • 雨天总有一种特有的能力,去牵引你的思绪。打开空间随意的翻看着每个人的小心事,高中很喜欢的语文老师发了一段...
    啊啾吧阅读 575评论 2 4