首页 > 编程语言 > 详细

JavaScript函数

时间:2019-10-17 00:27:14      阅读:63      评论:0      收藏:0      [点我收藏+]

普通函数

function demo(args){
  // ...
}

箭头函数

let demo = (args) => {
  // ...
}

普通函数与箭头函数的区别:

  • 普通函数中的this谁调用就指向谁;箭头函数的this是其父作用域。
  • 普通函数的this会被call / bind / apply等改变;箭头函数中的this不会被改变。

匿名函数

(function(){
 // ... 
})();

注意:

  • 匿名函数一般作为其它函数的形参、实参、返回值。
  • 匿名函数必须立即执行,不能只定义不执行。

函数的扩展运算符

function demo (...args){
  // ...
}

函数的扩展运算符将函数的实参打包成了一个数组,供开发人员操作。

注意:函数的扩展运算符只能在所有形参的末尾。

函数作用域

作用域

  • 全局作用域:在所有{}以外的区域
  • 块级作用域:除了函数内部外,其它{}内的区域
  • 局部作用域:函数内部的区域

不同作用域的区别

  • 全局作用域中,用varlet定义的变量都是全局变量
  • 块级作用域中,用var定义的变量是全局变量,用let定义的变量是局部变量
  • 局部作用域中,用varlet定义的变量都是局部变量

注意:在定义是省略varlet的变量,无论在哪个作用域定义的都是全局变量

函数预解析

预解析:

  • 浏览器在执行 javascript 代码时不会直接按照顺序执行代码,而是加工之后执行
  • 浏览器在加工 javascript 代码时会分为两步: 先预解析再按照书写顺序执行其他代码,这个加工的过程称之为预解析

预解析规则:

  • 直接使用function关键字定义的函数会被提升到这个js代码的顶部,无论在什么地方调用能够成功
  • 使用var定义变量并赋值的函数,由于var定能够以的变量存在变量提升,但赋值函数不会进行预解析,所以在函数定义之前调用变量值为undefined
  • 使用let定义变量并赋值的函数,由于let不存在变量提升,并且赋值函数不会进行预解析,所以在函数定义之前调用会报错

闭包函数

  • 闭包函数是一种特殊的函数,当一个函数使用了外部函数的数据(变量、函数),内部函数就会形成闭包。

  • 闭包的两个条件:函数嵌套、内部函数使用外部函数数据。

  • 闭包特点:只要内部函数还在使用外部函数,外部函数被使用的数据就不会被立即释放,所以闭包就可以延长外部函数数据使用周期。

  • 闭包特点:当后续不需要使用闭包时,一定要手动把闭包置为null,防止出现内存泄露

    let demo = () => {
        let num = 666;
        return () => {
            console.log(num);
        }
    }
    
    let fn = demo();
    fn(); // 666
    
    fn = null;

构造函数

工厂函数

工厂函数是一种特殊的函数,其目的是为了简化对象的创建

let createPerson = (name, age) => {
    return {
        name: name,
        age: age,
        say: function (){
          // ...
        }
    }
}
    
let p = createPerson('zs', 18);

构造函数

构造函数与工厂函数一样都是为了简化对象的创建,构造函数本质就是工厂函数的简化

构造函数与工厂函数的不同:

  • 构造函数的函数名首字母必须大写
  • 构造函数不能直接调用,必须使用new关键字
function Person (name, age){
    // let obj = new Object();
    // let this = obj;
    
    this.name = name;
    this.age = age;
    this.say = function (){
      // ...
    }
        
    // return obj;
    // 省略部分是JavaScript在执行过程中自行添加的
}
    
let p = new Person('zs', 18);

ES6中的构造函数

class Person(){
    constructor(name, age){
        // 构造函数中的代码块会在每次通过 new 创建对象时自动执行
        // 构造函数中的属性以及方法在每次创建对象时,都会在内存中重新开辟一块区域存放,每个实例对象之间的属性及方法相互独立、互不干扰
        
        this.name = name;
        this.age = age;
        this.say = function (){
            // ...
        }
        
        Person.countNum();
    }
    
    // constructor 外部的内容就相当于构造函数原型对象(prototype)上的内容
    // 在定义类时,在 constructor 外部使用 static 关键字定义的属性、方法称之为静态属性、静态方法
    // 静态属性、静态方法只会在类定义时生成一次,之后创建实例对象时不会重复生成,提高了代码的运行效率
    // 静态属性、静态方法只能通过 类名.属性名、类名.方法名 调用
    
    static count = 0;
            
    static countNum(){
        Person.count++;
    }
}

let p1 = new Person();
let p2 = new Person();

console.log(p1.say === p2.say); // false
console.log(Person.count); // 2

构造函数中的三角关系

  • 每个构造函数内部都有一个prototype属性,这个属性中保存了一个对象,称之为原型对象
  • 每个原型对象内部都有一个constructor属性,这个属性指向这个原型对象的构造函数
  • 实例对象内都有一个__proto__属性,这个属性指向实例对象构造函数的原型对象
function Demo(){
    // ...
}
    
let d = new Demo();

console.log(Demo.prototype.constructor === Demo); // true
console.log(d.__proto__ === Demo.prototype); // true

Function对象以及Object对象

  • Function构造函数是所有函数(包括构造函数)的祖先函数
  • Function构造函数的__proto__指向自身的原型对象
  • Object构造函数是所有自定义对象以及Function对象的祖先函数
  • Object构造函数的原型对象的__proto__指向 Null
    技术分享图片

原型链

原型链:在JavaScript中,当调用实例对象的属性或方法时会先从对象自身来查询;如未查询到则会沿着对象__proto__的指向依次查询,直到找到Object原型对象仍未找到时,由于Object原型对象的__proto__指向Null,则会报错。
技术分享图片

封装 、继承、多态

  • 封装

    • 当一个类将自身的成员变量暴露给外界,它就失去了对成员变量的控制权,外界可以随意修改器内部属性。

    • 封装本质就是对外界隐藏内部细节,仅提供必要的访问接口,以保证内部数据不被外部随意篡改。

    • 封装是面向对象设计的本质(将变化隔离),降低了数据被误用的可能性,保证了充分的安全性以及灵活性。

      function Person(){
          let age = 20;
      
          // 通过 setAge 方法,保证内部属性的有效性
          this.setAge = num => {
              age = (num > 0 && num <= 120) ? num : age;
          }
      }
  • 继承

继承就是将多个构造函数中共有的属性、方法提取出来放到父类函数中,从而减少代码的冗余度。

  // ES6中的继承
  class Person(){
      constructor(name, age){
          this.name = name;
          this.age = age;
          this.say = function(){
              // ...
          }
      }
  }
  
  class Student extends Person{
      constructor(name, age, score){
          // 通过 super 方法可以快速调用父类的构造函数,从而精简子类构造函数
          super(name, age);
          this.score = score;
          this.exam = function(){
              // ...
          }
      }
  }
  • 多态

    多态就是一种事物在不同环境下表现的状态不同

    • 在强类型语言(编译性语言,如 Java、c、c++)中,一般通过继承与多态来减少代码的冗余度
    • 在弱类型语言(解释性语言,如 JavaScript)中,由于变量的定义规则不严格,所以不需要使用多态

原型对象、对象类型的判断

  • 获取对象类型

    // 构造函数中存在一个 name 属性,存放着构造函数的名称
    // 由于原型对象中不存在 constructor 属性,所以会沿着原型链找到父类的构造函数
    console.log(p.__proto__.constructor.name === p.constructor.name); // true
  • 判断对象是否是构造函数的实例

    // instanceof 关键字会从原型链中寻找,只要构造函数出现在了实例对象的原型链上就会返回 true,若是查找到 Null 仍未查找到则会返回 false
    
    class Person{
        // ...
    }
    
    class Student extends Person{
        // ...
    }
    
    let stu = new Student();
    
    console.log(stu instanceof Student);// true
    console.log(stu instanceof Person);// true
    console.log(stu instanceof Object);// true
  • 判断构造函数的原型对象是否为对象的原型对象

    // isPrototypeOf 关键字会从原型链中寻找,只要构造函数的原型对象出现在了对象的原型链上就会返回 true,若是查找到 Null 仍未查找到则会返回 false
    
    class Person{
        // ...
    }
    
    class Student extends Person{
        // ...
    }
    
    let stu = new Student();
    
    console.log(Student.isPrototypeOf(stu));// true
    console.log(Person.isPrototypeOf(stu));// true
    console.log(Object.isPrototypeOf(stu));// true

JavaScript函数

原文:https://www.cnblogs.com/luwenfeng/p/11688625.html

(0)
(0)
   
举报
评论 一句话评论(0
关于我们 - 联系我们 - 留言反馈 - 联系我们:wmxa8@hotmail.com
© 2014 bubuko.com 版权所有
打开技术之扣,分享程序人生!