原生JS -- 关于面对对象,这篇解析得通俗易懂

1、引言

初学者在学习面对对象时可能总是会被绕晕,也理解不了面对对象中各种名词,但是了解这些概念在面对对象编程中极为重要,也意味着你能否学懂面对对象编程。下面我会利用已知探索未知去讲解面对对象,帮助大家理解。

2、创建对象

  • 不知道大家是否还记得,在创建数组时,有两种创建方式。一种是字面量创建,即let arr = [2, 3, 4];,另一种是构造函数创建,通过关键字new来创建,即let arr = new Array(2, 3, 4);
  • 这里的Array是一种内置构造函数。通过new执行构造函数,创建数组(数组也是对象)。
  • new执行方式:执行函数,凡是用new执行的函数,都叫构造函数。
  • 也就是说我们可以自己创建自定义构造函数。这里我们先说一下new的原理:
  • new的原理:
    1、创建了一个新对象
    2、修改了函数中的this指向,指向第1步创建的新对象
    3、检测原函数中是否主动返回对象,如果没有,那么返回第1~3步创建的对象
    4、并将新对象中的__proto__指向了该构造函数的prototype

先不用理解,继续向下看👇

  • 创建自定义构造函数:
function Fn() { // 行业规范,构造函数的函数名采用大驼峰式
    console.log(this);
}
Fn(); // 普通执行:this指向就是window
let f1 = new Fn(); // new执行:this的指向是new执行后构造函数的同名对象(new的原理第2点)
console.log(f1); // f1是new执行的返回值,函数同名对象 Fn{}(new的原理第1、4点)
let f2 = new Fn();
console.log(f1 == f2); // false 每次new执行创建的都是新对象,两两不相等
  • 1、每次new的执行都会创建一个新对象,即上述代码中的f1、f2,且这些对象两两不相等。
  • 2、通过new的执行修改了函数中this的指向,指向执行时创建的新对象,即指向上述代码中的f1、f2
  • 3、如果构造函数不主动返回对象,那它的返回值就是每次执行时创建的新对象
  • 4、(不急,这点需要结合原型)

循序渐进,来看看下面这段代码,看懂后我们再进行原型的介绍。

function Fn(n) { 
	// this指向对象,给new出来的对象添加属性
    this.name = n; // admin
}
let f1 = new Fn("admin"); // new执行时传参
console.log(f1); // Fn{name:"admin)

3、原型

  • 创建一个自定义构造函数Fn
function Fn(n) {
    this.name = n;
    this.show = function() {
         console.log(this.name);
    }
}
// 通过new创建一个对象
let f1 = new Fn("admin");
console.log(f1); // Fn {name: "admin", show: ƒ}
f1.show(); // admin
// 通过new再创建一个对象
let f2 = new Fn("root");
console.log(f2); // Fn {name: "root", show: ƒ}
f2.show(); // root
// 这两个对象不相等,且具有相同功能的show函数也不等
console.log(f1 == f2); // false
console.log(f1.show == f2.show); // false 两个对象的show方法不相等,与下面做对比👇
  • 与数组的构造函数式创建来对比
let arr1 = new Array(2, 3, 4);
let arr2 = new Array(4, 5, 6);
console.log(arr1 == arr2); // false
// push是数组的方法之一
console.log(arr1.push == arr2.push); // true 数组的方法是相等的,与上面做对比👆
  • 也就是说,到这一步我们的自定义构造函数并未创建成功,我们的需求是让console.log(f1.show == f2.show)的结果为true
  • 先卖个关子,给一个已知条件:内置构造函数创建的对象的方法,都被绑定在了自己的构造函数的原型对象(prototype)身上。
  • 比如数组的push方法,就被绑定在了数组的构造函数的原型对象身上Array.prototype.push
  • 先来看看这个prototype是个啥
console.log(Array.prototype); // 一个伪数组,包含了数组所有的方法
  • 继续使用数组来举例,我瞎编一个sayHello的方法,在arr1身上肯定没有sayHello这个方法,如果想通过arr1来执行sayHello方法
// 接上面代码
// arr1.sayHello(); // 如果直接通过arr1来执行这个方法,报错,必须先绑定在arr1身上
arr1.sayHello = function() {
	console.log("hello")
}
arr1.sayHello(); // hello
  • 如果我想让arr2也执行sayHello方法,也必须要再绑定再arr2身上,如果我想让arr3、arr4..等执行这个方法,就得一个一个绑定,很耗性能。
  • 但是,如果将这个方法绑定在数组的构造函数的原型身上,如下
Array.prototype.sayHello = function() {
	console.log("hello");
}
// 比如我想让arr2来执行sayhello(在上面的案例中,arr2未绑定此方法)
arr2.sayHello(); // hello
  • 执行成功了,就算创建无数个数组,它们都能执行sayHello这个方法,这个叫做"继承"。
  • prototype的原理:
    在对象自身拥有一个内置属性:proto,每当在构造函数的原型对象上如(Array.prototype)绑定一个方法时,都相当于在每个实例身上的__proto__这个属性里绑定了这个方法。
    * 这就是new原理的第4点。
  • 这个__proto__可以在浏览器上看到(注意proto左右是两个下划线),原型身上的属性或方法可以在这里找到

  • 由于一个实例至少会有两个__proto__(一个是自身的构造函数的,另一个是顶层原型对象的),所以原型也称为原型链
    在这里插入图片描述

  • 现在回到需求,如果把show方法绑定给自定义构造函数Fn的原型对象

function Fn(n) {
	this.name = n;
	// 不在构造函数内部设置show方法
	// this.show = function() {
	//     console.log(this.name);
	// } 
    }
    // 把show方法绑定给Fn的prototype
    Fn.prototype.show = function() {
        console.log(this.name);
    }

// 通过new创建一个对象
var f1 = new Fn("admin");
console.log(f1); // Fn {name: "admin", show: ƒ}
f1.show(); // admin,就算f1自身没有绑定show方法,仍能执行

// 通过new再创建一个对象
var f2 = new Fn("root");
console.log(f2); // Fn {name: "root", show: ƒ}
f2.show(); // root

console.log(f1 == f2); // false
console.log(f1.show == f2.show); // true,相等了
  • 相等了,也就是说我们的自定义构造函数创建成功了

至此,面对对象的基础使用就已经结束,下面总结面对对象的语法、代码及概念

4、总结

  • 语法
  • 面对对象的语法:
    1、属性写在构造函数内部的this身上
    2、方法写在构造函数的原型(prototype)上
    3、原型身上的方法在被实例执行时,其内部的this依然指向实例
  • 代码
function Fn(n) {
	this.name = n;
}
Fn.prototype.show = function() {
	console.log(this.name);
}
let f = new Fn("参数");
f.show();
  • 概念
    • 1、实例:上述所有通过new执行创建的对象,都称为实例
    • 2、原型:
      • 构造函数的prototype(显式原型)
        • 是当前构造函数身上的一个属性,自身是对象类型
        • 专门作为将来的实例的__proto__的指向
        • 所有添加给prototype的属性和方法,都能被将来的实例使用
      • 实例的__proto__(隐式原型)
        • 所有对象都具有的默认属性,自身是对象类型
        • 指向了:构造自身的构造函数的原型prototype
        • 当实例身上没有某个方法或属性时,默认查找__proto__的方法或属性
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值