JS函数详解(一)

脑图

在这里插入图片描述

1 创建


一般有三种函数的创建模式

1.1 函数声明


/* 如果有变量声明变量 */
function fun_name(a,b){
    /* code */
} // 注意这里无分号

1.2 函数表达式


const func_name = function(a,b){

}

1.3 构造函数

我们一般不使用构造函数来创建

			// 函数体为字符串
const func_name = new Function("console.log('hello world');")
func_name()

输出

hello world

2 调用


直接使用函数名 + 括号进行调用

2.1 语法


func_name()

调用之后,程序会直接执行函数体内的代码

2.2 参数


掉用的时候我们可以直接传递参数

  • 参数可以是任意数据类型,所以必要的时候我们需要对参数进行检查

  • 传递的参数多于定义的参数,多余的参数会被忽略

  • 传递的参数少于定义的参数,运算的时候会经过饮食类型转换

function sum(a,b){
    return a + b
}
console.log(sum(1,2,3))
console.log(sum(1))

输出

3
NaN

3 构造函数


构造函数其实与普通函数没有什么太大的区别,当使用方式不同的时候,功能就不同

  • 我们一般习惯的将构造函数名的首字母大写(当然你不大写也是可以的)
  • 调用方式决定了他是普通函数还是构造函数
    • 普通函数调用:func_name()
    • 构造函数调用:new Func_name()

3.1 简介


我们所创建的对象和函数等引用类型都会调用构造函数,比如

  • var arr = []var arr = new Array()的语法糖
  • var obj = {}var obb = new Object()的语法糖
  • ……

3.2 构造函数的执行流程


因为构造函数的执行需要new关键字,我们关注构造函数的执行流程,其实就是关注new关键字的作用

  1. 出现new关键字,会直接在对堆存中开辟一片空间,新建一个空对象
  2. 将空对象的__proto__指向构造函数的原型
  3. 将构造函数的this,通过apply指向空对象(新建的空对象设置为函数中的this)
  4. 返回该对象(返回实例对象)

使用伪代码来模拟new操作符

对于new函数的要求

  1. 传递参数的时候要传递构造函数的名字,eg:_new(Person,'Jerry',18)
  2. 可以只用 arguments来获取参数
function _new (){
    // 拿到构造函数的名称
    const Func = [].shift.call(arguments)
    // 1. 出现new关键字,会直接在对堆存中开辟一片空间,新建一个空对象
    const obj = {}
    // 2. 将空对象的`__proto__`指向构造函数的原型
    obj.__proto__ = Func.prototype
    // 3. 将构造函数的`this`,通过`apply`指向空对象
    Func.call(obj)
    // 4. 返回新创建的对象
    return obj
}

测试

function Person(name,age){
    this.name = name
    this.age = age
}
const person = _new(Person,"Jerry",18)
console.log(person)

输出

在这里插入图片描述

优化

const obj = {}他其实就是new Object()这里也用到了new,这样似乎不太好

const obj = {}
obj.__proto__ = Func.prototype
// 我们可以把上两句改成
const obj = Object.create(Func.prototype)//同样表示创建一个以Func.prototype为原形的空对象

3.3 构造函数与普通函数的区别


调用方式

  • 构造函数需要用new Func_name(args)来调用
  • 普通函数用func_name(args)调用

返回值

  • 构造函数会将创建的空对象this返回
  • 普通函数没有返回值,如果想要返回,必须使用return关键字

4 arguments


arguments他获取的是调用函数的时候传递的参数,并把这种参数封装成了一个伪数组,对于伪数组调用数组的方法 点击

function sum(){
    console.log(arguments)
}
sum(1,2,3,4)

输出

在这里插入图片描述

4.1 callee的使用


由上面的输出图我们发现了在 arguments 中有一个属性 calleecallee执行被调用的函数,也就是 sum本身

function sum(){
    console.log(arguments.callee) // 指向sum本身
    arguments.callee()// 递归调用sum()
}
sum(1,2,3,4)

5 构造函数与ES6 class


ES6的语法大多数都是语法糖,当然 class也是构造函数的语法糖

5.1 对于构造函数优化


当我们向构造函数中添加方法,我们每使用 new 创建一个对象,就会在堆内存中开辟一块空间去存储这个方法,所以对于方法的处理我们一般写成下面的样式

function Person (name,age){
    this.name = name
    this.age = age
    /* this.sayName = function(){
					console.log(this.name)
				} */
}
Person.prototype.sayName = function(){
    console.log(this.name)
}

把方法添加到原型中,避免上面的缺陷,他的实例都会拥有这个方法

5.2 类的使用


因为类是构造函数的语法糖,使用类我们可以很方便的实现对于构造函数的优化,节省空间

class Person {
    constructor(name,age) {
        this.name = name
        this.age = age
        console.log(arguments)
    }
    sayName(){ // 类的方法,不要加上function关键字
        console.log(this.name)
        console.log(this)
    }
}
const person = new Person("Jerry",18)
person.sayName()

输出

在这里插入图片描述

通过上面的例子我们就发现,他依旧具有构造函数的所有的特性

5.3 类的说明


  1. constructor属于构造器,用来接收参数
  2. this就相当于构造函数中的this,他所指向的是实例对象
  3. 在类中添加的方法会直接添加到原型上,注意不要使用function关键字
  4. 构造函数可以当做普通函数使用,但是类不可以,他就是构造函数的语法糖

JS在执行的时候,也会把类转化为构造函数来执行,而类中的extends,也是为了实现原型链上面的继承

6 原型与原型链


每一个函数都有原型对象,每一个函数都有 prototype属性

6.1 原型对象


  1. 函数的 prototype属性会指向一个空对象,这个对象就是原型对象
  2. 原型对象 中有一个constructor属性,这个属性反过来指向函数
function fn(){
    console.log('hello world')
}
const proto = fn.prototype
console.log(typeof proto) // object
console.log(proto) // {constructor: ƒ}
console.log(proto.constructor) //fn constructor指向函数
proto.constructor()//通过原型调用函数

输出

在这里插入图片描述

原型对象图示

在这里插入图片描述

6.2 原型对象的使用


我们一般往原型对象中添加方法,这样他的实例都会自动拥有添加的方法(上面构造函数的优化)

Person.prototype.func_name = function(){ code }

6.3 原型的分类


原型分为显式原形和隐式原形

显式原形

  1. 每个函数都有一个显式原型,即prototype
  2. 在定义函数的时候自动添加的,默认是一个空的Object对象
  3. 没有链

隐式原形

  1. 每一个 实例对象 都有一个隐式原型,即__proto__
  2. 创建对象的时候自动添加,默认值为构造函数的prototype的值
  3. 我们所说的原型链,其实就是隐式原型形成的链

我们一般操作显式原型

6.4 原型链


__proto__形成的链,当我们访问一个对象的属性或者方法的时候

  1. 他会先在自身的属性中寻找,如果找到则返回
  2. 如果没有,则沿着__proto__这条链向上查找,查找到则返回
  3. 如果没找到,返回undefined

6.4.1 instanceof 的使用


语法

A instanceof B

如果B函数的显式原形对象在A对象的原型链上,返回true,否则返回false(简写A是否为B的实例)

function Fn(){}
const fn = new Fn()
console.log(fn instanceof Fn) // true

6.4.2 原型链图解


在这里插入图片描述

在图中一共有三列,最左边的一列为 实例对象,中间的一列为 构造函数,最右侧的一列为 原型对象

只有实例才拥有__proto__属性,只有函数才拥有prototype属性,原型对象拥有constructor指针

  • 对于prototypeconstructor两个属性较容易理解
  • 主要考虑__proto__属性
    1. 最左栏为相应构造函数的实例对象,所以有__proto__指向器原型对象
    2. 中间栏为函数,函数拥有prototype属性,但为什么会拥有__proto__呢?拥有__proto__说明他是某个对象的实例,请记住下面两句话
      • 所有的对象都是Object()的实例
      • 所有的函数都是Function()的实例(包括他自身)
    3. Object()是一个函数,那么他就是Function()的实例,而__proto__指向原型,所以会有从Object()__proto__指向Function的原型
    4. 同理,Foo()Function()本身就是函数,也同样有__proto__指向Function的原型
    5. 最右侧一列为原型对象,要记住这样一句话
      • 函数的显式原型所指向的原型对象,默认是空Object的实例对象,所以原型对象间有指向Object.prototype__proto__
      • 但是对于Object()函数并不满足
    6. Object()的原形对象就是原型链的尽头,沿着他的隐式原型往上找到的是null返回 undefined
console.log(Object instanceof Function)
console.log(Object instanceof Object)
console.log(Function instanceof Function)
console.log(Function instanceof Object)
/* 以上四句全部打印为 true,证明了第2,3,4个结论 */
function Fn(){}
console.log(Fn.prototype instanceof Object) // true
console.log(Object.prototype instanceof Object) // false
console.log(Function.prototype instanceof Object) // ture
/* 以上三句证明了结论5*/
console.log(Object.prototype.__proto__) //null

6.4.3 总结


对于原型链的总结
  1. 实例只有隐式原型(我们的对象{},数组[]等都是实例)
  2. 函数既有显式原形又有隐式原型
  3. 原型对象只有隐式原型
对与原型链继承总结
  1. 原型上的属性会因为先后顺序而被覆盖
  2. 普通构造函数的实例不会指向Function.prototype
function A(){}
A.prototype.n = 1
const b = new A()

A.prototype = {
    n:2,
    m:3
}
const c = new A()
console.log(b.n,b.m,c.n,c.m) // 1 undefined 2 3
function Fn(){}

Object.prototype.sayName = function(){
    console.log("sayName")
}
Function.prototype.sayAge = function(){
    console.log("sayAge")
}

const fn = new Fn()
fn.sayName() // sayName
// fn.sayAge() 报错
Fn.sayName() // sayName
Fn.sayAge() // sayAge
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值