金色小芝麻

vuePress-theme-reco 金色小芝麻    2021 - 2023
金色小芝麻 金色小芝麻

Choose mode

  • dark
  • auto
  • light
主页
分类
  • JavaScript
  • BUG复盘
  • SVG笔记
  • TypeScript
  • 个人总结
  • CSS笔记
  • 开发工具
  • 前端入门
  • Vue2.0
  • 性能优化
  • 架构学习
  • 每日一题
标签
时间轴
社交
  • 掘金 (opens new window)
author-avatar

金色小芝麻

83

文章

27

标签

主页
分类
  • JavaScript
  • BUG复盘
  • SVG笔记
  • TypeScript
  • 个人总结
  • CSS笔记
  • 开发工具
  • 前端入门
  • Vue2.0
  • 性能优化
  • 架构学习
  • 每日一题
标签
时间轴
社交
  • 掘金 (opens new window)
  • 前端入门

    • 博客搭建过程及相关配置
    • 数组中的16种常用方法
    • JS中的数据类型简析——基本数据类型值
    • JS中的数据类型object基础
    • JS中三种常见的判断
    • 数据类型之间的区别(堆内存Heap VS 栈内存Stack)
    • JS基础练习题及解析
    • 开关灯的小案例
    • JS中数据类型检测方法——typeof
    • 条件判断几个小练习
    • JS循环——for循环
    • 自定义属性实现选项卡小案例
    • 字符串中的12种常用方法
    • DOM操作的简单原理
    • JS实现隔行变色鼠标跟随小案例
    • JS中function的基础知识
    • JS中数组去重的三种方法
    • JS中时间格式化的三种方法
    • JS中URL参数处理的三种方法
    • JS小案例——获取随机验证码
    • DOM操作之——获取DOM标签的9种方式
    • DOM操作之——DOM节点类型及属性
    • DOM操作之——元素的增删改、样式修改、插入内容
    • JS中THIS相关问题梳理
    • JS中的变量提升机制
    • 在全局:私有上下文中:带VAR和不带VAR的区别
    • 作用域和作用域链查找机制
    • JS中堆栈内存的释放问题
    • JS中的闭包机制
    • ES3和ES6定义变量的区别
    • JS中的单例和工厂设计模式
    • JS中的面向对象OOP理论基础
    • 构造函数创建自定义类
    • JS中的原型和原型链
    • Math中常用的9种方法
    • 从一道阿里经典面试题剖析函数三种角色
    • 重写内置call
    • 重写一个内置new
    • 浏览器渲染页面的主体流程
    • 减少DOM的回流和重绘
    • JS中的多种继承方式
    • JS中数据类型检测四种方式的优缺点
    • JS中的正则表达式&&全面梳理
    • 非严格模式 🆚 严格模式的区别
    • 数组方法reduce、filter、flat
    • 获取数组中最大值/最小值的三种基础方法
    • 轮播图——渐隐渐显版
    • 深克隆 VS 浅克隆|深比较 VS 浅比较|回调函数
    • 轮播图——左右切换版
    • 事件及事件绑定、事件对象及事件传播
    • 从在地址栏输入网址到看到页面的过程&&AJAX基础
    • 想自学JS吗?想提升JS底层原理吗?76张脑图带你彻底搞懂原生JS
    • 面试手写API系列
    • 30张脑图带你从零开始学VUE
    • JS中的盒子模型
    • 初识JS-基础中的基础
    • 前端发展简史
    • JS中的三大类输出方式

构造函数创建自定义类

vuePress-theme-reco 金色小芝麻    2021 - 2023

构造函数创建自定义类

金色小芝麻 2020-05-30

上篇我们说到,当我们需要自己做一些事情的时候,我们自己基于构造函数,创建的类就是自定义类;

这篇我们就来说说什么是构造函数。

# 思维导图

# 一、构造函数语法

构造函数,字面上的意思理解:

  • “构造”:就是通过某种手段或者方法,创造出来(重构出来);
  • “函数”:就是我们之前学过的函数

这么一想,构造函数 的意思就是通过某种手段或者方法,创造出来(重构出来)一个函数

那么怎么创建呢?

首先我们来看一个👇普通函数

function Func(name, age) {
    this.name = name; 
    this.age = age;
}
let f1 = Func('金色小芝麻', 18);//=> 把Func函数执行(当做普通函数执行)
//=>方法中的THIS:window
console.log(f1); //=>undefined 因为没有返回值
console.log(window.name, window.age); //=>'金色小芝麻'  18

这是我们的普通函数执行,在上面的代码的基础上我们加个new,一切就都变了

function Func(name, age) {
    this.name = name; 
    this.age = age;
}
let f1 = new Func('金色小芝麻', 18);
console.log(f1); //=> {name: "金色小芝麻", age: 18}
console.log(window.name, window.age); //=> window.name是空  window.age是undefined

此时我们很明显发现不一样了,由window.name不在是‘金色小芝麻’, window.age不在是‘18’,我们可以断定,函数体中的this绝不在是window了,而且在没有return的情况下f1不在是undefined了;

这种在函数执行前加一个new的方式,就是我们的构造函数执行;


  • 在ES3语法中:
    • new 函数() => 这种方式就是基于构造函数的方式来执行
    • 约定的语法规范:类名的第一个字母一般都是大写的

就像是这样👇:

function Func(name, age) {
    this.name = name; 
    this.age = age;
}
let f1 = new Func('金色小芝麻', 18);

此时:

    1. Func不在被誉为普通函数,而是叫做构造函数(也就是我们所谓的自定义类)
    1. 返回的结果也不再基于RETURN来判断返回值,返回的结果是当前类的一个实例
想创建自定义类和创建类的实例,只需要在执行的时候 不在 "`函数()`" 普通函数执行;
而是 "`new 函数()`" 执行,也就是构造函数执行,这样方法被称为类,返回结果被称为类的实例;

# 构造函数语法与普通函数语法的区别

function Fn() {
	this.x = 100;
	this.y = 200;
}
let f1 = new Fn();
console.log(f1);
let f2 = new Fn;
console.log(f2); 
  • 普通函数
    • Fn 是函数本身(不是执行)
    • Fn() 函数执行
  • 构造函数
    • new Fn(); 构造函数执行 =>有参数new
    • new Fn; 构造函数执行(这种方式不能给函数传递实参了) =>无参数new

# 二、构造函数执行与普通函数执行的区别

还是以下👇题为例:

function Func(name, age) {
    this.name = name; 
    this.age = age;
}
let f = Func('小芝麻', 18);
let f1 = new Func('小芝麻', 18);

为了方便理解,直接看图

# 1、普通函数执行时

还是以上体为例:这里我直接省略全局下的代码操作过程,直接看函数执行是的私有作用域中的操作过程;

  • 第一步:初始化作用域链:<EC(FUNC),EC(G)>
  • 第二步:初始化THIS:window
  • 第三步:形参赋值:name = '小芝麻' ; age = 18
  • ......省略不重要的步骤。
  • 第四步:变量提升:无
  • 第五步:代码执行:
    • this.name = name; //=> window.name = '小芝麻'
    • this.age = age;//=> window.age= 18

没有返回值,执行完成出栈销毁:f = undefined

# 2、构造函数执行时

构造函数拥有普通函数执行的特征,也有自己单独的一些操作

  • 第一步:初始化作用域链:<EC(FUNC),EC(G)>
  • 第二步:形参赋值:name = '小芝麻' ; age = 18
  • 第三步:变量提升:无

以上三步说明了构造函数具备普通函数的一面;

构造函数执行时浏览器多做的一些事情👇:

  • 第四步:在当前上下文中,创建一个对象(这个对象就是当前类的实例)=> AAAFFF111(我们假设的空间地址)

  • 第五步:让当前上下文中的THIS指向新创建的实例对象:this :AAAFFF111

  • 第六步:代码执行

    • this.name = name; //=> 把私有变量的name值,赋给新创建实例对象的私有属性
    • this.age = age;//=> 把私有变量的age值,赋给新创建实例对象的私有属性
  • 第七步:代码执行完,如果我们没有设置RETURN,浏览器默认会把新创建的实例对象返回

相信看到这里你已经明白其中的道理了,我们在简单总结下:

function Func(name, age) {
	/*
	 * 代码执行之前,创建一个实例对象(堆)
	 * 让THIS指向实例对象
	 */
	this.name = name; //=>this.xxx=xxx 都是在给实例对象设置私有的属性和方法
	this.age = age;
	/*
	 * 如果函数没有return,默认会把创建的实例对象返回 
	 */
}
let f1 = new Func('小芝麻', 18);
let f2 = new Func('金色', 10);
console.log(f1);//=> {name: "小芝麻", age: 18}
console.log(f2);//=> {name: "金色", age: 10}
console.log(f1 === f2); //=>FALSE 每次都是创建一个新的实例,每一个实例和其他实例都是一个单独的对象(个体),互相之间不冲突

总结如下:

# 3、构造函数执行时:实例的相关问题

  • 1、构造函数执行,由于具备普通函数特征,所以在私有上下文中可能会出现一些私有变量,但是这些私有变量和实例没有必然的联系,私有上下文中的THIS才是实例,所以只有写THIS.XXX=XXX的操作,才是给实例设置私有属性;
    • 实例的私有属性和上下文中的私有变量不是一个东西
  • 2、当前类的每一个实例都是单独的一个对象,实例和实例之间是独立的
  • 3、在构造函数的函数体中,基于 THIS.XXX=XXX 给实例设置的属性和方法都是自己私有的,和其它实例中的属性和方法不冲突

# 4、构造函数执行时:return的相关问题

function Fn() {
	this.x = 100;
	this.y = 200;
	return 1;
}
let f1 = new Fn;
console.log(f1); //=>{x: 100, y: 200} 仍然返回当前实例

function Fn() {
	this.x = 100;
	this.y = 200;
	return {
		name: 'xxx'
	};
}
let f1 = new Fn;
console.log(f1); //=>{name:'xxx'} 不再是Fn类的实例

总结:

  • 1、没有return,默认返回当前类的实例对象(对象数据类型)
  • 2、有return,并且返回基本类型值,最后的返回结果还是类的实例,不会有影响
  • 3、如果返回的是一个引用数据类型,则会把默认返回的实例给覆盖掉,这样我们接收结果就不在是当前类的实例了,而是自己返回的值 => 真实项目中,如果想创建类的实例,则建议大家不要在手动写return了,防止实例被覆盖

# 三、一道例题

function Fn(x) {
	let y = 20;
	this.total = x + y;
	this.say = function () {
		console.log(`${x} + ${y} = ${this.total}`);
	};
}
let f1 = Fn(10); //=>f1=undefined
let f2 = new Fn(10); //=>f2实例对象
let f3 = new Fn(20); //=>f3实例对象

console.log(f2.total); //=>30
console.log(f2.x); //=>undefined
console.log(f3.y); //=>undefined
console.log(f1 === f2); //=>FALSE
console.log(f2 === f3); //=>FALSE
f2.say(); //=>"10+20=30"
console.log(f2.say === f3.say); //=>FALSE  都是当前实例的私有属性和方法(所有出现在构造函数的函数体中的 this.xxx=xxx 操作,都是设置私有的属性和方法)
console.log(f1.total); //=>Uncaught TypeError: Cannot read property 'total' of undefined  f1不是对象,只有对象才能操作键值对
console.log(window.total); //=>30  忽略上面的报错后输出结果
window.say(); //=>"10+20=30"  忽略上面的报错

# 四、检测实例的几种方法

# 1、instanceof:检测当前实例是否属于这个类

instanceof: 检测当前实例是否属于这个类(或者检测当前值是否为某个类的实例)

  • 语法: 值 instanceof 类

  • 返回值: 是它的实例返回TRUE,反之返回FALSE

    (当前类的原型只要出现在了实例的原型链上就返回true,原型链概念我们下一篇讲解)

    function Fn() {}
    let f1 = new Fn;
    console.log(f1 instanceof Fn); //=>TRUE
    console.log(f1 instanceof Array); //=>FALSE
    
  • 局限:instanceof不适用于基本数据类型检测,要求检测的实例必须是对象数据类型的

    console.log(100 instanceof Number); //=>FALSE
    
  • 应用场景:区分对象中的特殊数据格式,例如数组或者正则

    let arr = [10, 20];
    let reg = /^$/;
    // console.log(typeof arr); //=>"object"  typeof不能具体检测对象类型的细分
    // console.log(typeof reg); //=>"object"
    console.log(arr instanceof Array); //=>TRUE
    console.log(arr instanceof RegExp); //=>FALSE
    

# 2、hasOwnProperty:检测当前属性是否为实例私有属性

hasOwnProperty:检测当前的某一个属性是否为实例(或者对象)的私有属性

  • 语法:对象.hasOwnProperty(属性)
  • 返回值:是私有的属性返回TRUE,如果不是对象的属性或者不是私有的属性都返回FALSE

# 3、in检测当前属性是否为对象的属性

in:检测当前属性是否为对象的属性

  • 语法:属性 in 对象

  • 返回值:只要是对象的属性(不管是公有还是私有的属性)结果都是TRUE

    function Fn() {
    	// 构造函数体中出现的 this.xxx = xxx 都是给当前实例设置的私有属性
    	this.x = 100;
    	this.y = 200;
    	this.say = function () {
    		console.log(x + y);
    	};
    }
    let f1 = new Fn;
    let f2 = new Fn;
    console.log(f1.say === f2.say); //=>false
    
    console.log(f1.hasOwnProperty('say')); //=>true
    console.log(f1.hasOwnProperty('name')); //=>false 因为你连这个属性都没有
    console.log(f1.toString()); //=>"[object Object]"  toString一定是f1对象的属性,否则f1也不可能调用这个方法
    console.log(f1.hasOwnProperty('toString')); //=>false  toString不是它的私有属性,是他的公有属性
    
    console.log('say' in f1); //=>true
    console.log('toString' in f1); //=>true
    console.log('name' in f1); //=>false
    

利用上面两个方法我们可以自己写一个检测某一个属性是否为当前对象的公共属性的方法

# 需求:检测某一个属性是否为当前对象的公共属性(是他的属性,但是还不是私有的)

function Fn() {
    	// 构造函数体中出现的 this.xxx = xxx 都是给当前实例设置的私有属性
    	this.x = 100;
    	this.y = 200;
    	this.say = function () {
    		console.log(x + y);
    	};
    }
    let f1 = new Fn;
    let f2 = new Fn;
// 检测ATTR是否为OBJ的公有属性
function myHasPublicProperty(attr, obj) {
	// 1.需要是它的属性 =>IN检测为TRUE
	// 2.不是它的私有属性 =>HASOWNPROPERTY检测为FALSE
	// return ((attr in obj) === true) && (obj.hasOwnProperty(attr) === false);或者
	return (attr in obj) && !obj.hasOwnProperty(attr);
}
console.log(myHasPublicProperty('say', f1)); //=>false
console.log(myHasPublicProperty('name', f1)); //=>false
console.log(myHasPublicProperty('toString', f1)); //=>true
欢迎来到 金色小芝麻
看板娘