构造函数建立自定义类

上篇咱们说到,当咱们须要本身作一些事情的时候,咱们本身基于构造函数,建立的类就是自定义类;javascript

这篇咱们就来讲说什么是构造函数。java

思惟导图

1、构造函数语法

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

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

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

那么怎么建立呢?bash

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

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,一切就都变了ui

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了;this

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


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

就像是这样👇:3d

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

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

仍是如下👇题为例:

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

为了方便理解,直接看图

一、普通函数执行时

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

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

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

二、构造函数执行时

构造函数拥有普通函数执行的特征,也有本身单独的一些操做

  • 第一步:初始化做用域链:<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 每次都是建立一个新的实例,每个实例和其余实例都是一个单独的对象(个体),互相之间不冲突
复制代码

总结以下:

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

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

四、构造函数执行时: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类的实例
复制代码

总结:

  • 一、没有return,默认返回当前类的实例对象(对象数据类型)
  • 二、有return,而且返回基本类型值,最后的返回结果仍是类的实例,不会有影响
  • 三、若是返回的是一个引用数据类型,则会把默认返回的实例给覆盖掉,这样咱们接收结果就不在是当前类的实例了,而是本身返回的值 => 真实项目中,若是想建立类的实例,则建议你们不要在手动写return了,防止实例被覆盖

3、一道例题

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" 忽略上面的报错
复制代码

4、检测实例的几种方法

一、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
    复制代码

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

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

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

三、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
复制代码
相关文章
相关标签/搜索