> 文章列表 > this的指向、工厂方法创建函数、构造函数

this的指向、工厂方法创建函数、构造函数

this的指向、工厂方法创建函数、构造函数

一、this的指向(耐心看完,这个彻底理解,才不会成为一时的记忆)

1.我们先来看函数的参数:

  function fn(a,b ) {//形参console.log(a, b);//a:1,b:2}fn(1, 2)//实参

这没有问题的,其实函数的形参a,b就相当于声明了两个变量a,b,所以我们传入实参数值的时候就相当于给变量赋值,那么就能打印到a,b的值了

那么,函数fn,形参我传多余的参数,a,b,c,d,而实参呢,只有两个值,那么c,d打印的会是多少啊?

是undefined对不对啊,那么为什么呢,是因为声明了4个变量,只给前两个变量赋值,那么后两个变量声明了但没有赋值,我们是不是打印undefined

多说几句哈:

形参少传:就一个a, console.log(a, b);肯定b报错了啊,b没有声明,会报b没有被定义的错误

实参多传:那就打印a,b的值了,实参相当于赋值,连变量都没有,赋值给谁,打印谁啊,对吧

实参少传:实参就传一个值,其实实参少传不就是形参多传嘛,其实我们只去思考声明变量和赋值,就能知道打印结果了

2、什么是this?

this他就是一个参数,是一个函数内部隐含的参数,人家内部给你声明好的一个参数

function fn(a,b){console.log(a,b)//a:1,b:2console.log(this)//window
}
fn(1,2)

 指向:一个对象——函数执行的上下文对象

指向:根据函数调用方式的不同,指向不同

1.以fn()函数的形式调用,this永远指向window

function fn() {console.log(this);
}
fn()//Window

2.以obj.sayHi()方法的形式调用,this指向调用方法的那个对象,如obj

function fn() {console.log(this);
}       
let obj = {name: '张三',sayHi: fn
}
obj.sayHi() //this 就是 obj

 以方法的形式调用,哪个对象调用的这个方法,这个方法的this就是哪个对象
 

3、箭头函数的this

 箭头函数:没有自己的this 没有arguments、但是有剩余参数...arr

 使用场景:适合替代匿名函数

自己没有,找他的上一级

   // 箭头函数的this:自己没有,找他的上一级const fun = () => {console.log(this); //window}fun() //不是因为window.fun(),而是因为找父级作用域的this指向

对象方法的箭头函数this :

        // 对象方法的箭头函数thisconst obj = {name: 'h',sayHi: () => {console.log(this); //window,找的上一级的指向,找的就是window}}obj.sayHi()// 对象方法的箭头函数thisconst obj2 = {name: 'j',sayHi: function() {console.log(this); //this:obj2let i = 1const count = () => {console.log(this); //this:obj2}count()}}obj2.sayHi()

DOM事件回调函数推荐不使用箭头函数,使用箭头函数,他就不指向DOM对象了,指向window

二、工厂方法创建函数

1、我们先创建几个对象:

 let obj = {name: '小新',age: 5,class: '向日葵小班',sayName: function() {alert(this.name);}}let obj2 = {name: '风间',age: 5,class: '向日葵小班',sayName: function() {alert(this.name);}}let obj3 = {name: '妮妮',age: 5,class: '向日葵小班',sayName: function() {alert(this.name);}}
obj2.sayName()

这样创建对象,重复性代码有点多,看起来较笨重,有没有利用重复性的代码呢

可以考虑写一个函数封装起来,然后调用一次是不是就生成了一个对象啊,就不用复制三分再去改了。

2.工厂方法创建函数

工厂方法创建函数就是一个函数,他能创建一个对象,理解工厂:批量的生产出来对象

function createPerson (name,age){//第一步:要new 一个对象let obj=new Object()//第二步:要给对象里添加点属性obj.name=name,obj.age=ageobj.sayName=function() {alert(this.name)}//第三步:返回值return obj
}//第四步:调用函数
let obj=createPerson("小新♥",5)
let obj2=createPerson("妮妮",5)
let obj3=createPerson("风间",5)
console.log(obj,obj2,obj3)

 这样就优雅的创建了三个对象,那么这里的this,指向哪里呢?

是不是以方法的形式调用的函数啊,那函数的this指向谁,obj3调用的就指向obj3了 ,所以函数sayName里的alert方法执行,this.name就是obj3对象里的name,就是风间了。

 三、构造函数

1.什么是构造函数?

就是一个普通的函数,不过调用的方式不同

普通函数调用:函数名()

构造函数调用:new 函数名()

另外,写法上,常首字符大写(规范,不是规定),最好首字母大写哈

    function Person(name, age) {this.name = namethis.age = age this.sayName = function() {alert(this.name)}}// 调用方式不同let p = new Person('风间', 5)let p2 = new Person('妮妮', 5)console.log(p, p2); //p 就是一个对象,只不过他有名字,名字就是那个函数的名字

我们先看函数,先忽略里面的this

 这回对象p,p2是不是有名字了啊

2.构造函数的this

        // 看this:那就要看构造函数的执行流程了

        // 1.你new 的方式调用了这个函数,那么(浏览器)就立即创建一个新的对象

        // 2.(浏览器)将新创建的对象设置为函数中的this

        // 3.逐行执行函数中的代码(代码咱们写的)

        // 4.(浏览器)将新建的对象作为函数返回值返回

总而言之:

this的指向:谁调用的函数,指向谁!!

以函数调用,是不是window.函数名()啊,谁调用的:window,指向谁:指向window

那么构造函数谁调用的:new 函数名(),指向谁:new 函数名(),他把他赋值给变量p,那么变量p,就是new 函数名(),那么p就是对象,this就指向p

3.类、类的实例对象

这计算机概念性名字听起来挺难理解哈,别怕,他就是个名字,我们来理解他

使用同一个构造函数创建的对象,我们称为一类对象( p,p2是一个类的,d,d2是一个类的

也将这个构造函数称为一个类(如:构造函数Person称为类,构造函数Dog称为类,这就是两个类)

将通过一个构造函数创建的对象,称为是该类的实例

谁是通过构造函数创建的对象:p p2 d d2  ,这玩意叫实例,p p2 d d2叫实例

(如:p,p2,d,d2是实例,p,p2是Person类的实例,d,d2是Dog类的实例)

补充:

   // instanceof 检查一个对象是否是一个类的实例console.log(p instanceof Person); //trueconsole.log(d instanceof Person); //false// Object 是所有对象的祖先// 任何对象 instanceof Object 都会是trueconsole.log(p instanceof Object); //trueconsole.log(d instanceof Object); //true

4.问题

 // 问题:什么叫类?这个构造函数称为一个类

 // 什么叫实例?实例就是一个对象,构造函数干嘛的?创建对象的,通过构造函数创建出来的对象

 // 什么叫构造函数?new 出来的一个函数

// 构造函数与普通函数的区别?调用方式不同

//构造函数的this指向? 谁调用的指向谁,构造函数的this指向他的实例对象。

5.补充

在构造函数内部创建了一个方法sayName,每个实例对象p1,p2,p3就都有了自己独立的方法,那么创建1000个实例对象,就会有1000个方法,这1000个方法都一摸一样,完全为了实现一个功能,这样太消耗内存了

 // 构造函数function Person(name, age) {this.name = namethis.age = agethis.sayName = function() {alert(this.name)}}let p1 = new Person('小新', 5)let p2 = new Person('妮妮', 5)let p3 = new Person('风间', 5)// p1.sayName()// p2.sayName()// p3.sayName()console.log(typeof p1.sayName); //functionconsole.log(p1.sayName == p2.sayName); //false

把函数放到全局上,他就只有这么一个函数,提升性能,创建1000个实例对象,还是这一个方法

        function Person(name, age) {this.name = namethis.age = agethis.sayName = fn}let p1 = new Person('小新', 5)let p2 = new Person('妮妮', 5)let p3 = new Person('风间', 5)function fn() {alert(this.name)}console.log(p1.sayName == p2.sayName); //true

但是这样写是有问题的,污染了全局作用域的命名空间,别人在写个函数叫fn,就会覆盖你的fn了,所以我们把他写在原型上

function Person(name, age) {this.name = namethis.age = age// this.sayName = fn}let p1 = new Person('小新', 5)let p2 = new Person('妮妮', 5)let p3 = new Person('风间', 5)Person.prototype.sayName = function() {alert(this.name)
}p1.sayName()
console.log(p1.sayName == p2.sayName); //true