// 定义空间
function Person(name) {
this.name = name; // 人的名字
// 可以说话
this.say = function () {
console.log(`我的名字:${this.name}`);
};
// 可以吃饭
this.eat = function () {
console.log(`今天我要吃黄焖鸡`);
};
}
var person = new Person('Maic');
var person2 = new Person('张三');
var actionsA = '星辰变';
function test() {
var actions = '完美世界';
console.log(actionsA); // 星辰变
}
test();
console.log(actions); // Uncaught ReferenceError: actions is not defined
面向对象对每一个程序员来说,非常熟悉,在 C 语言里,我们说它是面向过程,在
java
中我们熟悉的面向对象的三大特征中封装
、继承
、多态
,java
是高级语言,在BS
架构中,后端语言用java
等语言运行在服务器上,而在离用户端最近的B
端,js
中也有面相对象。今年回家又相亲吗?在过年回家的路上,我们来聊聊我理解中的面相对象,这个对象比较轻松,那个悲伤的话题打住,正文开始...
在
js
中申明一个对象我们可以 🈶️ 以下几种方式:::: details code
:::
构造函数
我们用以上申明对象,其实第一种与第三种是一样的,通常来讲第一种方式用的多,两者构造函数都是
Object
,你可以理解第一种方式是第三种方式的简写。而第二种方式
function Animal
这是申明一个构造函数,一般构造函数都是大写字母开头,为了与普通函数的区别,在我没有new
的时候,它就是个普通函数,但是如果我对它进行了new Animal
操作,那么此时,性质就变味了,此时我变成了一个对象。第四种是
es6
的一种新的方式,本质上可以理解为定义构造函数的变体。但是class
这种方式让你组织你的代码更加优雅。js
语言借鉴了java
思想,但又与java
还是有些不同,有人把js
定义为解释性语言,就是不需要编译,直接在浏览器端引入一段脚本就能跑,当然底层的那些是chrome
内核帮我们做了解析。对于web
开发者来说,我只要保证写的js
脚本能跑通就行。既然借鉴了
java
的对象思想,那么又是如何体现?设计语言的大师把现实中所有物质,一切皆可用对象来描述。我们可以把这个对象理解成一个抽象的空间,而这个空间里有人,人有名字,可以吃饭,可以说话等等。
在代码中,我是如何去描述呢?我们先用用第二种方式构造函数去描述
::: details code
::: 我们可以测试一下脚本,将这段代码 copy 到控制台上可以知道 ::: details code
::: 在控制台上,我们可以验证对象的构造函数是谁? ::: details code
:::
从上面例子我们已经知道构造函数有个特点:
1、内部有
this
,这个this
其实指向的就 new 操作后的实例对象2、生成对象时,必须
new
构造函数在我们用
new
操作后,这个person
对象就具备了空间属性,有名字,可以说话,可以吃饭,而通常我们把名字比喻成属性,说话和吃饭就是动作,可以比作方法。在面相对象中,描述一个事物的特征有两个特性,对象属性和方法。而对象属性和方法,在面相对象中有私有属性、公有属性、私有方法,公用方法、以及静态方法、并且还可以继承,有了这些、从而实现了封装、继承、多肽。从而让代码变得更抽象、更模块化、更易于维护。
有人说代码写得好的,就像是在写诗,因为没有一句废话、高度复用,可扩展性强,健壮、抽象,在你读优秀框架作者的源码时,你会就发现,世界就是你,你就是世界。
new
在我们
new
构造函数后,我们探究下,这个new
背后做了啥? ::: details code:::
没有
new
时,直接把Person
当方法了,我们看下打印结果 不可思议的就是这个方法内部的this
指向的是全局window
对象。这里扩展一点,我们用
var person = Person('Maic');
实际上就是用var
这个关键词在全局作用域
下开辟了一块空间。其实function fnName()
也是开辟了一个局部作用域空间。用不同的关键词定义就形成特殊的空间,因为还有块级作用域
一说。在这个未使用
new
操作符的普通函数,内部的this
指向就是那个被调用者。在你定义函数,定义变量时,我们可以看下那个隐藏的被调用者究竟是谁? ::: details code::: 我们发现 3 打印的是
true
,但是函数内部打印的 this 并不等于window
我们要知道函数内就是一个独立的作用域,在函数内
var
定义变量就是一个私有的,如果你想在函数外部访问,对不起,没门,函数内部可以访问外部变量,但是函数内部变量不能在外部访问,举个例子,理解下 ::: details code::: 不出意外,
actions
提示为未定义,因为函数内作用域的属性,无法直接被外部访问。但是函数外部变量,却可以在内部访问,因为函数外部的变量能被局部作用域访问。
你可以把定义函数的区域理解成一个独立城堡,而函数外部就是城门外面,只进不出。 ::: details code
::: 我们举例这么多就是为了验证函数那句
window === this
为false
,其实函数内部的this
不是由函数自己内部而定义,它的指向是函数真正被调用那个对象。 ::: details code::: 等价于 ::: details code
::: 所以函数内部指向的是 window,所以你可以看到,
window.xiaoqi
就是函数内部的this.xiaoqi
,而内部定义的局部变量var xiaobai
打印却是undefined
,后续可以写一篇关于作用域的理解。这里发散得有点远。 ::: details code::: 如果我想要一个函数可以当成一个正常的对象用,那要怎么办呢? ::: details code
::: 另外有一点要注意,在严格模式下,函数内部
this
不能指向全局那个被调用的对象,因为此时this
指向的是undefined
,而undefined
不能动态添加属性。 ::: details code::: 在了解没有
new
操作背后,那个this
就是指向函数的被调用者。那么用new
后呢。我们打印一下
new Person('石昊',1)
我们仔细发现,
t
这个实例对象的构造函数就是Person
,我们可以总结以下几点 1、创建一个空间、返回一个对象实例 ::: details code::: 2、将空间对象的原型指向构造函数的
prototype
::: details code::: 3、指定内部
this
对象,构造函数内部的this
就是t
::: details code::: 4、执行构造函数体内部代码
在构造函数内部,我们没有任何返回值,当实例化后,当前构造函数的 this 就是那个实例对象,如果我返回是其他对象呢? ::: details code
::: 在
new
构造函数,如果构造函数没有返回任何值,那么就是new
实例返回始终是一个对象。如果返回的是非对象,那么会忽略。 ::: details code:::
实现 new
以上我们已经知晓了
new
的操作步骤,现在有个面试题,实现一个new
操作符。笔者在以前面试题被问了这个问题后,曾经一脸懵,我回答面试官,
new
就是一个关键字,怎么实现,这是他语法规定的啊?我心中万马奔腾,但是这肯定不是他想要的答案,直到今日终于可以手写一个了new
操作符了。 我们仔细观察下下面的原生new
的过程 ::: details code::: 下面开始了 ::: details code
:::
new
操作后,实际上实例对象的隐式__prototype__
指向的就是构造函数Person的Prototype
简式声明对象
说完了
new
操作符,来了解下项目中高频创建对象 ::: details code:::
总结
1、面向对象思想,具有一个抽象事物描述事情的特征,属性方法。有
java
继承、封装思想。2、函数作用域概念,在函数作用域内部,可以访问外部函数变量,但是函数外部无法访问函数内部变量。
3、构造函数内部 this 指向,在
new
后,对象实例的__prototype__
指向的就是构造函数的prototype
,当前构造函数内部this
指向的就是构造函数的实例对象。4、
new
实现原理,本质上就是返回一个对象,将该对象的隐式原型指向构造函数。5、常见的几种申明对象。
6、本文示例code-example