IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> JavaScript知识库 -> JS构造函数和原型链的个人理解(面向对象编程) -> 正文阅读

[JavaScript知识库]JS构造函数和原型链的个人理解(面向对象编程)

对象创建

按照一贯所说的那样,万物皆对象,也就是在JavaScript(以下简称JS)所有的引用数据类型都是对象

而对象,都是由构造函数创建的,也就是说,对象都是由函数创建

使用new关键字加上一个函数名,就可以得到一个实例对象(箭头函数除外)

     let o = new Object();
      let o2 = {}; // new Object()

      let map = new Map();
      let set = new Set();

      console.log(typeof map);
      console.log(typeof set);

像代码段中所示,用new Object();就得到了一个对象的实例对象(对于第二行代码中的字面量创建,其实可以视为在隐性地调用一个new Object()函数)

new Map()就能得到一个Map数据结构的实例对象


通过工厂函数进行进一步理解

工厂函数

在现实生活中的工厂中,一般对于单个工厂而言,他生产出来的就是自己这个阶段意义上的成品,比如说制衣厂生产的是衣服,制造纽扣的工厂生产出来的就是纽扣(也许在整个生产链上面不是成品,但是对于自己的这一部分来说就是成品)

所以工厂函数的作用,就是用于对象创建,可以隐藏复杂的构建细节

工厂的特点是可以批量生产

以下模拟一个口红生产工厂

    function Lipstick(colorNumber, size, weight, price) {
      let lipstick = new Object();
      lipstick.colorNumber = colorNumber;
      lipstick.size = size;
      lipstick.weight = weight;
      lipstick.price = price;
      return lipstick;
    }
    let Dior772 = Lipstick("Dior772", "8CM", "3g", "280");
    console.log(Dior772);

在这个函数中,创建一个对象

对象中的属性,(.colorNumber色号,.size尺寸等等)是口红这个类的属性

对象中的属性值,(Dior772,8CM等)是这一只口红的这些属性对应的属性值

其实是调用Lipstick这个函数,传入参数,然后在函数里面实现了对象的创建,最后返回这个对象,赋值给Dior772这个变量,也就是Dior772这个变量中存储的是一个对象,这个对象会随着实参的变化而产生变化

可以理解为一个口红生产工厂叫做Lipstick,然后它接到了来自品牌的订单,给了它原材料以及生产要求,生产要求之类的就是实参,然后这个工厂在自己的内部创建了一个生产链,用生产要求这些数据去把口红实例化,变成一个具象的口红,再返回自己这个生产链生产出来的东西,给到品牌方,品牌方给这个口红命名为Dior772,就是一个完整的过程

一些工厂函数比如document.createElement("div")就是创建了一个div标签


构造函数

终于要讲到构造函数了

上文有提到,JS中所有的对象都是由构造函数创建

而在面向对象编程语言(比如Java)中,对象是由类创建

所以为了模拟后端语言的一些方法,我们这里JS中的构造函数可以被看做是类

构造函数中,采用大驼峰命名

像刚刚所说,构造函数的作用是用来创建对象,new这个关键字,作用是在堆中开辟一个存储空间,创建一个新对象,然后把地址存储到栈中

对于一个函数来说,它是普通函数还是构造函数,不取决于如何定义这个函数,而是取决于我们如何调用函数

调用函数的时候,如果函数名前面有关键字new,就说明这个函数作为一个构造函数使用(就想嘛,一个函数在我们定义的时候就已经存在了,那用new后面加一个函数是啥意思,不就是像创建数组创建对象一样,用这个函数创建一个新的东西,那这个东西既然是用和数组和对象一样的方式创建的,说明数据类型也一样,数组/对象都是对象,所以说就是这样子创建了一个新的对象)

值得注意的一点是,

如果这个函数是个普通的函数,那么它的函数体中的this指向的是调用者window

如果这个函数是作为构造函数,那么函数体中如果出现this指针,那么这个指针指向的是新创建的对象的地址(体现在控制台上的是构造函数)

    function Fn() {
      console.log(this);
    }
    Fn();//window
    let fn1 = new Fn();//Fn
    let a = null;
    function Phone() {
      console.log(this);//Phone
      a = this;
    }

    let p = new Phone();
    console.log(p === a);//true
    console.log(typeof p);//object

从这一段代码可以看出来,实际上是构造函数在创建这个对象的时候,在堆里面开辟了新的空间,然后把地址返回出来,存到p里面,而this指针指向的也是这个空间的地址,所以在每次new Phone()的时候,它的this动态地指向当前的地址。那么当然a(存储这个地址)和p(存储这个地址)是相等的

私有属性的定义

function Foo(){
    this.f="aaaa";
}
let foo=new Foo();
foo.f="bbbb";
console.log(foo);//"bbbb"
let foo1=new Foo();
console.log(foo1);//"aaaa"

这段代码就是说,在每次创建一个新的对象的时候,这个对象的地址所对应的空间中就会放一个属性f,属性值是"aaaa",而每次的新的地址都是不一样的,也就是说每个对象都独立地具有了这个属性和属性值,所以在第五行代码中,修改了foo这个对象中的属性值,不会影响到其他的foo1之类的,而只是修改了自己的空间中的属性值

下面这个可以更直观的感受到

    function Foo(a, b, c) {
      // 构造函数中的this 指向新创建的对象的地址
      // this中所有的属性都是私有属性
      // 每一个实例都可以获得这些私有属性
      this.a = a;
      this.b = b;
      this.c = c;
    }

    let f1 = new Foo(1, 2, 3);
    console.log(f1);//a:1,b:2,c:3

    let f2 = new Foo(1, 4, 7);
    console.log(f2);//a:1,b:4,c:7

    let f3 = new Foo();
    console.log(f3);//a:undefined,b:undefined,c:undefined

那么同理,添加在this关键字上的方法叫做私有方法

    function Lipstick(colorNumber, size, weight, price) {
      this.colorNumber = colorNumber;
      this.size = size;
      this.weight = weight;
      this.price = price;

      this.ddl = function (ddlDate) {
        console.log(`保质期至${ddlDate}`);
      }
    }
    let Dior772 = new Lipstick("Dior772", "8CM", "3g", "280");
    console.log(Dior772);
    Dior772.ddl("2022-3-5");//保质期至2022-3-5

    let Lancome888 = new Lipstick("Lancome888", "9CM", "2.8g", "300");
    console.log(Lancome888);
    Lancome888.ddl("2023-8-1");//保质期至2023-8-1

    console.log(Dior772.ddl);
    console.log(Lancome888.ddl);
    console.log(Dior772.ddl === Lancome888.ddl);//false

最后三行代码,虽然this.ddl的函数体是一样的,但是因为this指向不一样,对比的时候拿出地址中的方法去比较,是false


公有属性

添加在this关键字上的叫做私有属性,但是可能会造成资源浪费

所以使用到了prototype(原型对象)

原型对象是函数的一个子属性,所有的函数都具有这个属性

而每个原型对象都具有一个指针,叫做constructor,

首先抛开prototype和constructor两个单词的含义

函数 -> 函数.prototype? ?从这个点操作符就可以看出来,prototype是函数的一个属性

那么属性这个词的意思可以帮助我们理解逆向关系,只有一个函数存在了,才会进一步有它的属性,也就是说属性是从函数中来的,也就能够理解为什么prototype会有一个指针指向函数

对于函数.prototype? 译为原型对象,我是这样理解的,比如现在有一个电视剧里的角色,她有原型,原型呢就是一个现实生活中真实存在的人,那么可以认为这个角色她本身存在是有多个属性的,比如她的身高体重年龄性别,放在对象中更好理解一点

const role={
    height:170,
    weight:50,
    sex:"female".
    age:18,
    prototype:"翠花"
}

那么我们可以很清楚的看出来,为什么说原型是她的一个属性

那么反过来,constructor就是这个角色,因为有这个角色,才会有这个角色的属性(才会有原型这一说,否则这个人就只是这个人,不叫原型),直接讲的话就是说因为有函数,才会有函数的属性,所以constructor直译为建造者(例子可能不是很恰当,但是我只能想到这样了)

原型对象的作用是 存储构造函数实例对象的公有属性

如果函数不作为构造函数使用,原型对象将没有意义

原型对象中的所有属性,都可以被构造函数的实例对象所访问

也就是可以直接理解成,原型对象是实例对象的父级,里面可以访问到外面,外面不能访问到里面(原型对象是构造函数的属性,所以原型对象和构造函数是平级的,而实例对象是由构造函数创建的,所以实例对象是构造函数的子级,所以实例对象是原型对象的子级)

    function Lipstick(colorNumber, size, weight, price) {
      this.colorNumber = colorNumber;
      this.size = size;
      this.weight = weight;
      this.price = price;

      Lipstick.prototype.used = function () {
        console.log("被使用了");
      }
    }
    let Dior772 = new Lipstick("Dior772", "8CM", "3g", "280");
    console.log(Dior772);
    Dior772.used();

?那么图中的__proto__(左右各是两个下划线)

定义:__proto__属性是一个静态的指针,始终指向了当前对象构造函数的原型对象

JavaScript中所有的对象 都拥有一个 __proto__ 属性

对于实例对象b,很好理解,它的构造函数是function Baz,构造函数的原型对象就是Baz.prototype

对于function Baz,函数的本质也是对象,函数的构造函数也是函数,是function Function,那function Function的原型对象是Function.prototype

对于最上面的函数和函数.prototype,后者是一个原型对象,对象是由function Object构造的,所以prototype的构造函数是funciton Object,那么它的__proto__指针就指向function Object的原型对象,也就是Object.prototype

总体来说,就是从里往外,如果实例对象中有这个属性,就用这个,如果没有,往外出,去原型对象中找,如果还没有,再往外,去Object.prototype里面找,再没有,就undefined

  JavaScript知识库 最新文章
ES6的相关知识点
react 函数式组件 & react其他一些总结
Vue基础超详细
前端JS也可以连点成线(Vue中运用 AntVG6)
Vue事件处理的基本使用
Vue后台项目的记录 (一)
前后端分离vue跨域,devServer配置proxy代理
TypeScript
初识vuex
vue项目安装包指令收集
上一篇文章      下一篇文章      查看所有文章
加:2021-11-11 12:37:51  更:2021-11-11 12:39:19 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年5日历 -2024/5/10 8:59:05-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码