一、前言

上个月底,爸爸因为事故突然离世,说心里话,现在看到'去世','爸爸'这样的字眼,眼泪都会忍不住在眼眶打转,还是需要时间治愈。最近也只是零碎的看了下东西,始终沉不下心去读书,直到今天还是决定捡起之前看的JS模式。

前面两篇博客大概记录了书中前两章节我觉得一些好用的知识,从这篇开始就是第三章--直接量和构造函数了,难度也不算大,最近下班了在公司花点时间慢慢写。

从第三篇开始,我想在介绍每个知识点前,先以概要的形式将这一部分介绍的东西以问题的形式列出来,方便大家带着问题去读,读完了再回头看问题,看自己能不能答出来,这样也方便检验自己对于知识的掌握情况。

二、对象直接量

概要:什么是对象,什么是对象直接量写法,空对象不是真正的空对象

我们可以将js中的对象简单理解为名值对组成的散列表,其中值都是名的属性,值可以是原始值(string,number...),也可以是对象,而当对象是函数时,我们一般称之为方法。

js中自定义的对象任何时候都是可变的,内置本地对象的属性也是可以修改的,比如你可以先创建一个空对象,然后在给它添加一些属性或方法。而在创建对象时,对象直接量写法是较为理想的方式。

var me = {};
me.name = "echo";
me.getName = function () {
return me.name;
};

你可以删除对象的某个属性或方法

delete me.name;

其实我们创建一个对象也没必要像上面先创建空对象,再一步步添加属性方式,在对象创建时可以同时把你需要定义的属性同时添加好。

let me = {
name = "echo",
getName = function () {
return this.name;
}
};

那么这种直接用 = 创建对象的方式就是对象直接量的写法,很直接不是吗。对象直接量语法包括:

• 将对象主体包含在一对花括号内。
• 对象内的属性或方法之间使用逗号分隔。最后一个名值对后也可以有逗号,但 在IE下会报错,所以尽量不要在最后一个属性或方法后加逗号。
• 属性名和值之间使用冒号分隔
• 如果将对象赋值给一个变量,不要忘了在右括号之后补上分号

我在上方提到的空对象可以说是算是一种简称,它们并不是真正的空对象,即便申明一个{},它也会从Object.prototype继承很多属性和方法,但是我们其实有方法创建严格意义上的空对象,我在上一篇文章中就列出了两种方法,有兴趣可以去看看。

三、通过构造函数创建对象

概要:为什么推荐对象直接量写法而不是构造函数写法

尽管JS中没有类的概念,但当你想快速创建多个具有共同特征的实例时还是可以使用构造函数,JS中内置了不少构造函数,例如Object(),Date(),String()等等。

我们用构造函数的写法来创造上面的对象:

var me = new Object();
me.name = "echo";
me.getName = function () {
return me.name;
}

相比之下,对象直接量的写法与构造函数写法相比代码更少,推荐直接量写法的还有两个原因

一是它可以强调对象是一个简单的可变的散列表,而不必一定派生自某个类。

二是当你使用Object()创建对象时,解析器需要顺着作用域链开始查找,直到找到Object构造函数为止,而直接量的写法是不会存在作用域解析行为。

 四、自定义构造函数

概要:当你new一个构造函数时发生了什么?

除了对象直接量和内置构造函数之外,我们还可以通过自定义的构造函数来创建实例对象,像这样。

var Person = function () {
this.name = "echo";
this.sayName = function () {
console.log('my name is '+ this.name);
};
}
var me = new Person();
me.sayName();//my name is echo

说个小插曲,这里我自定义的构造函数名Person的字母P其实可以小写,但我们都知道,内置构造函数都是大写开头,所以为了让构造函数更为醒目,推荐首字母大写!

很奇怪对不对,我们new一个构造函数得到一个实例,这个实例就继承了构造函数的属性方法,那new这个过程中到底发生了什么?

1.创建一个空对象,将它的引用赋给this,继承函数的原型。

2.通过this将属性和方法添加至这个对象。

3.最后返回this指向的新对象。

我们用代码模拟这三句话,就像这样:

var Person = function () {
// var this = {};
this.name = "echo";
this.sayName = function () {
console.log('my name is '+ this.name);
};
// return this; 这里隐性返回的其实就是上面创建的空对象,这个空对象被赋予了name属性和一个sayName方法
}
var me = new Person();
me.sayName();//my name is echo

在这段代码中,sayName()方法被添加到了this中,但有个问题,不管我们执行几次new Person(),sayName()方法会反复的被添加到this中,且每次sayName()方法都会在内存中新开内存。

当我们所有实例中的sayName()方法都是一模一样时,这种做法是很浪费内存的,推荐做法是将sayName()方法添加在Person的原型中。

var Person = function () {
this.name = "echo";
};
Person.prototype.sayName = function () {
console.log('my name is '+ this.name);
};
var me = new Person();
me.sayName();//my name is echo

关于new一个构造函数到底发生了什么,我在前面说会隐性的新建一个空对象赋予this,还是那句话,这里的空对象并不是严格意义上的空,它还是继承了Person的原型,准确来说应该是这样。

// var this = Object.create(Person.prototype);

 五、构造函数的返回值

概要:构造函数能返回什么?默认返回什么?

当我们new一个构造函数总是会返回一个对象,默认返回this所指向的对象。如果我们没有在构造函数内为this赋予任何属性,则会返回一个集成了构造函数原型,没有自己属性的'空对象'。(如果读不懂这句话,请结合new发生的过程去理解)

尽管我们没在构造函数内写return语句,也会隐性的返回this,但其实我们可以返回自定义的对象。像这样:

var Person = function () {
this.name = "echo";
var that = {};
that.name = "wl";
return that;
};
var me = new Person();
me.name;//wl

构造函数可以返回任意对象,只要你返回的是个对象。假设你返回的不是对象,程序也不会报错,但这个返回值会被忽略,最终还是隐性的返回this所指向的对象。

var Person = function () {
this.name = "echo";
var name = "wl";
return name;
};
var me = new Person();
me.name; //echo

六、强制使用new 的模式

概要:不使用new调用构造函数会怎样?构造函数内能自定义对象吗?不使用new也能继承构造函数原型的做法

构造函数与普通函数无异,只是调用需要使用new,当我们不使用new调用时,语法也不会出错,但函数中的this会指向全局对象(非严格模式是window)。

var Person = function () {
this.name = "echo";
};
var me = Person();
window.name//echo

在这段代码中实际上创建了一个全局对象属性name,你可以通过window.name访问到它。那么说到这里对于构造函数我们强调两点。

1.构造函数名首字母大写

2.调用构造函数使用new

遵守这些约定肯定是好的,但在实际开发的构造函数中,我们常常看看使用that等其它字面量代替this的做法。这么做的目的是为了确保构造函数按照自己定义的方式执行,而不存创建空对象赋予this等隐性不可见的行为,更可预测。

var Person = function () {
var that = {};
that.name = "echo";
return that;
};
var me = new Person();
me.name;//echo

对于上述代码中,我们使用that代替了this,使用that只是一种命名约定,你可以使用self,me甚至任意非js语言保留字的字段。

或者that都不创建,直接返回一个对象。

var Person = function () {
return {
name : "echo"
};
};
var me = new Person();
var you = Person();
me.name//echo
you.name//echo

这种写法不管我们是否使用new去调用,都能得到一个实例,但这种模式丢失了原型,所有的实例都不会继承Person()原型上的属性。

var Person = function () {
return {
name:'echo'
}
};
Person.prototype.sayName = function () {
console.log(1);
}; me.sayName();//报错,自定义对象未指向Person,没继承Person的方法
you.sayName();//报错,同上

我们在前面说,不使用new时,this指向window(非严格模式),无法继承Person的任何属性。

var Person = function () {
this.name = "echo"
};
Person.prototype.sayName = function () {
console.log(1);
};
var me = new Person();
var you = Person();
me.name;//echo
you.name;//报错,此时的name是window的属性
me.sayName();//
you.sayName();//报错,在实例化过程中this指向window,并未继承Person的方法

那有办法可以让不使用new情况下实例也能继承Person属性的做法吗,当然有,比如调用自身的构造函数:

var Person = function () {
if(!(this instanceof Person)){
return new Person();
}
this.name = "echo"
};
Person.prototype.sayName = function () {
console.log(1);
};
var me = new Person();
var you = Person();
me.name;//echo
you.name;//echo
me.sayName();//
you.sayName();//

看到没,没使用new的实例you也继承了Person的属性和方法,如果看不懂那应该是对于instanceof运算符不太了解,这里顺带说下

instanceof运算符用于测试构造函数的prototype属性是否出现在对象的原型链中的任何位置---MDN

object instanceof constructor   object:要检测的对象.    constructor:某个构造函数

function Car(make, model, year) {
this.make = make;
this.model = model;
this.year = year;
}
var auto = new Car('Honda', 'Accord', 1998);
console.log(auto instanceof Car);//true
console.log(auto instanceof Object);//true

那么上述代码中,new Person()好理解,this就是隐性返回的实例,this instanceof Person为true跳过判断,直接走new一个构造函数时发生的过程,得到实例自然会继承Person的属性和方法。

Person()呢,this指向window,很明显this instanceof Person为false,假假为真,执行判断内的代码new Person();同理,也走了new过程的三部曲,得到的实例也继承了Person的属性和方法。

有疑问或者错误也欢迎大家指出来。

最近总是觉得没什么值得开心的事情,趁着双十二,给自己换了个键盘,毕竟每天都是要写代码的,也算换一个心情。

尽管除了吃饭交房租就没什么开销,还是挺心疼的。

如果大家对于new过程还有疑惑,以及如何实现一个new方法,欢迎阅读博主这篇文章 js new一个对象的过程,实现一个简单的new方法

那么就先写到这里了。

精读JavaScript模式(三),new一个构造函数居然发生了什么?的更多相关文章

  1. 精读JavaScript模式(七),命名空间模式,私有成员与静态成员

    一.前言 惰性十足,这篇2月19号就开始写了,拖到了现在,就是不愿意花时间把看过的东西整理一下,其它的任何事都比写博客要有吸引力,我要反省自己. 从这篇开始,是关于JS对象创建模式的探讨,JS语言简单 ...

  2. 精读JavaScript模式(八),JS类式继承

    一.前言 这篇开始主要介绍代码复用模式(原书中的第六章),任何一位有理想的开发者都不愿意将同样的逻辑代码重写多次,复用也是提升自己开发能力中重要的一环,所以本篇也将从“继承”开始,聊聊开发中的各种代码 ...

  3. 精读JavaScript模式(一)

    一.前言 为什么读这本书? 其实做前端开发,一个需求给不同工作经验的人去做,只要完工时间不算苛刻,大家都是能实现的.功能实现虽然大致相同,但当我们回归代码去看实现方式,代码书写的美观程度,以及实现的方 ...

  4. 精读JavaScript模式(二)

    我在想知识点怎么去分类,原本计划一章节一篇,但这样会会显得长短不一.更主要的是看到哪写的哪更为随意.那么这一篇还是紧接第一篇进行知识梳理,上篇说到了更优化的for循环,现在继续聊聊其它的循环方式. 1 ...

  5. 精读JavaScript模式(六),Memoization模式与函数柯里化的应用

    假期就这么结束了!十天假就有三天在路上,真的难受!想想假期除了看了两场电影貌似也没做什么深刻印象的事情.流浪地球,特效还是很赞,不过对于感情的描写还是逃不掉拖沓和尴尬的通病,对于国产科幻还是抱有支持的 ...

  6. 精读JavaScript模式(四),数组,对象与函数的几种创建方式

    一.前言 放了个元旦,休息了三天,加上春运抢票一系列事情的冲击,我感觉我的心已经飞了.确实应该收收心,之前计划的学习任务也严重脱节了:我恨不得打死我自己. 在上篇博客中,笔记记录到了关于构造函数方面的 ...

  7. 精读JavaScript模式(五),函数的回调、闭包与重写模式

    一.前言 今天地铁上,看到很多拖着行李箱的路人,想回家了. 在上篇博客结尾,记录到了函数的几种创建方式,简单说了下创建差异,以及不同浏览器对于name属性的支持,这篇博客将从第四章函数的回调模式说起. ...

  8. Javascript模式(三) 策略模式

    var data = { "username" : "zhangsan", "password" : "12345690" ...

  9. 《JavaScript模式》第3章 字面量和构造函数

    @by Ruth92(转载请注明出处) 第3章:字面量和构造函数 一.创建对象的三种方式 // 对象字面量 var car = {goes: "far"}; // 内置构造函数(反 ...

随机推荐

  1. 利用maven将项目打包成一个可以运行的独立jar包

    目标:希望把Java项目打包成一个完整的jar包,可以独立运行,不需要再依赖其他jar包. 我们在用eclipse中mvn创建mvn项目的时候,选择非webapp,会默认的以jar打包形式,如下图: ...

  2. 关于requests库中文编码问题

    转自:代码分析Python requests库中文编码问题 Python reqeusts在作为代理爬虫节点抓取不同字符集网站时遇到的一些问题总结. 简单说就是中文乱码的问题.   如果单纯的抓取微博 ...

  3. s11.9 sar:收集系统信息

    功能说明: 通过sar命令,可以全面地获取系统的CPU.运行队列.磁盘I/O.分页(交换区).内存.CPU中断和网络等性能数据. 语法格式 sar  option interval count sar ...

  4. line number is important in Exceptions.

    行号作为debug信息 在出现异常时可以迅速定位 package ztest; public class Test { public static void main(String[] args) { ...

  5. 11-22 ArrayList

    自己定义一个类 public class Student{ public String name; //属性 public void fangfa(){ //方法 } } 在另一个java文件里可以调 ...

  6. 【repost】javascript:;与javascript:void(0)使用介绍

    有时候我们在编写js过程中,需要触发事件而不需要返回值,那么就可能需要这样的写法 最近看了好几个关于<a>标签和javascript:void(0)的帖子,谨记于此,以资查阅. 注:以下代 ...

  7. Log4Cpp的使用(转)

    本文介绍如何使用Log4CPP. Log4Cpp介绍 Log4Cpp的Api接口可以在http://log4cpp.sourceforge.net/api/index.html中查询得到. Log4C ...

  8. 消息中间件——activeMQ

    Activemq使用教程 解压activmq进入bin\win64 启动activemq.bat 启动成功 浏览器访问http://127.0.0.1:8161 创建maven工程 在pom.xml中 ...

  9. git 删除本地分支、远程分支、本地回滚、远程回滚

    一. git 删除分支 1. git 删除本地分支 git branch -D branchname 2. git 删除远程分支 git push origin :branchname (origin ...

  10. java visualVM(jconsole)远程监控服务器java进程

    1. JMX方式(jconsole也可通过此方式进行连接) jmx方式能监控到CPU信息,但无法使用visualVM的visualVM GC插件    jmx无密码方式 监控普通的java进程 . 设 ...