上篇咱们说到,当咱们须要本身作一些事情的时候,咱们本身基于构造函数,建立的类就是自定义类;javascript
这篇咱们就来讲说什么是构造函数。java
构造函数,字面上的意思理解:数组
- “构造”:就是经过某种手段或者方法,创造出来(重构出来);
- “函数”:就是咱们以前学过的函数
这么一想,构造函数 的意思就是经过某种手段或者方法,创造出来(重构出来)一个函数浏览器
那么怎么建立呢?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
new 函数()
=> 这种方式就是基于构造函数的方式来执行就像是这样👇:3d
function Func(name, age) {
this.name = name;
this.age = age;
}
let f1 = new Func('金色小芝麻', 18);
复制代码
此时:
Func
不在被誉为普通函数,而是叫作构造函数(也就是咱们所谓的自定义类)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);
复制代码
为了方便理解,直接看图
仍是以上体为例:这里我直接省略全局下的代码操做过程,直接看函数执行是的私有做用域中的操做过程;
<EC(FUNC),EC(G)>
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
了,防止实例被覆盖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" 忽略上面的报错
复制代码
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(属性)
TRUE
,若是不是对象的属性或者不是私有的属性都返回FALSE
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
复制代码