理解JavaScript面向对象编程思想

这篇博客深入探讨了JavaScript的面向对象编程,包括一切皆对象、原型对象、继承机制、执行上下文和this的关键概念。讲解了对象创建、函数与对象的关系、原型链、instanceof运算符以及多种继承方式,如原型链继承、构造函数继承、组合继承、原型式继承等。此外,还讨论了执行上下文的词法作用域、执行上下文和闭包,以及this的四种取值情况。博客内容适合JavaScript开发者和面试准备者阅读。
摘要由CSDN通过智能技术生成

0 写在前面的话

大多数的面向对象编程语言中,比如C++和Java,在使用他们完成任务之前,必须创建类(class)。但在JavaScript中并不需要或者说不强制使用类。
面向对象都有如下几种特性:

  • 封装
    数据可以和数据操作的功能组织在一起

  • 聚合
    一个对象可以引用另一个对象

  • 继承
    一个新创建的对象和另一个对象拥有同样的特性,而无需显示复制功能

  • 多态
    一个接口可以被多个对象实现

1 一切都是对象

“一切都是对象”这句话的重点在于如何去理解“对象”这个概念。
——当然,也不是所有的都是对象,值类型就不是对象。

首先咱们还是先看看javascript中一个常用的运算符——typeof。typeof应该算是咱们的老朋友,还有谁没用过它?

typeof函数输出的一共有几种类型,在此列出:

console.log(typeof x); // undefined 
console.log(typeof 10); // number
console.log(typeof 'abc'); // string
console.log(typeof true); // boolean
console.log(typeof function () {}); //function 
console.log(typeof [1, 'a', true]); //object
console.log(typeof { a: 10, b: 20 }); //object 
console.log(typeof null); //object 
console.log(typeof new Number(10)); //object

以上代码列出了typeof输出的集中类型标识,其中上面的四种(undefined, number, string, boolean)属于简单的值类型,不是对象。剩下的几种情况——函数、数组、对象、null、new Number(10)都是对象。他们都是引用类型,也就是我们说的对象。

1.1 对象的创建

ES5里面有两种方法可以创建对象,或者说实例化对象

new构造函数
对象字面量
在ES6当中, 允许直接写入变量和函数,作为对象的属性和方法。这样的书写更加简洁。

var birth = '2000/01/01';
var Person = {
  name: '张三',
  //等同于birth: birth
  birth,
  // 等同于hello: function ()...
  hello() { console.log('我的名字是', this.name); }
};

1.2 函数和对象的关系

函数是一种对象,但是函数却不像数组一样——你可以说数组是对象的一种,因为数组就像是对象的一个子集一样。但是函数与对象之间,却不仅仅是一种包含和被包含的关系,函数和对象之间的关系比较复杂,甚至有一点鸡生蛋蛋生鸡的逻辑。

        function Fn() {
            this.name = '张三';
            this.year = 1988;
        }
        var fn1 = new Fn();

这个例子很简单,它能说明:对象可以通过函数来创建。对!也只能说明这一点。
但是我要说——对象都是通过函数创建的——有些人可能反驳:不对!因为:

var obj = { a: 10, b: 20 };

其实以上代码的本质是:

var obj = new Object();
        obj.a = 10;
        obj.b = 20;

所以,可以很负责任的说——对象都是通过函数来创建的。现在是不是糊涂了—— 对象是函数创建的,而函数却又是一种对象。天哪!函数和对象到底是什么关系啊?

2 原型对象

我们可以把原型对象看作是对象的基类。几乎所有的函数都有一个名为prototype的属性,该属性是一个原型对象用来创建新的对象实例。所有创建的对象实例共享该原型对象。

2.1 prototype属性

这个prototype的属性值是一个对象(属性的集合,再次强调!),默认的只有一个叫做constructor的属性,指向这个函数本身。

在这里插入图片描述

原型既然作为对象,属性的集合,不可能就只弄个constructor来玩玩,肯定可以自定义的增加许多属性。例如这位Object大哥,人家的prototype里面,就有好几个其他属性。

在这里插入图片描述

一个对象实例通过内部属性[[prototype]]追踪原型对象。该属性是一个指向该实例使用的原型对象的指针,当new一个新的对象时,构造函数的原型对象就会赋给该对象的prototype属性。

 function Fn() { }
        Fn.prototype.name = '张三';
        Fn.prototype.getYear = function () {
            return 1988;
        };

        var fn = new Fn();
        console.log(fn.name);
        console.log(fn.getYear());

Fn是一个函数,fn对象是从Fn函数new出来的,这样fn对象就可以调用Fn.prototype中的属性。

因为每个对象都有一个隐藏的属性——“proto”,这个属性引用了创建这个对象的函数的prototype。即:

fn.proto === Fn.prototype

2.2 隐式原型 proto

每个函数function都有一个prototype,即原型。这里再加一句话——每个对象都有一个proto,可成为隐式原型。
该属性没有写入 ES6 的正文,而是写入了附录,原因是__proto__前后的双下划线,说明它本质上是一个内部属性,而不是一个正式的对外的 API,只是由于浏览器广泛支持,才被加入了 ES6。标准明确规定,只有浏览器必须部署这个属性,其他运行环境不一定需要部署,而且新的代码最好认为这个属性是不存在的。因此,无论从语义的角度,还是从兼容性的角度,都不要使用这个属性,而是使用下面的Object.setPrototypeOf()(写操作)、Object.getPrototypeOf()(读操作)、Object.create()(生成操作)代替。
在实现上,__proto__调用的是Object.prototype.proto

在这里插入图片描述

那么上图中的“Object prototype”也是一个对象,它的proto指向哪里?
好问题!

在说明“Object prototype”之前,先说一下自定义函数的prototype。自定义函数的prototype本质上就是和 var obj = {} 是一样的,都是被Object创建,所以它的proto指向的就是Object.prototype。
但是Object.prototype确实一个特例——它的__proto__指向的是null,切记切记!
在这里插入图片描述

对象的proto指向的是创建它的函数的prototype,就会出现:Object.proto === Function.prototype。用一个图来表示。
在这里插入图片描述

上图中,很明显的标

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值