资讯专栏INFORMATION COLUMN

JS之函数(1)

宋华 / 2682人阅读

摘要:前言这段时间突然发现原生好多东西都忘记了但有些东西确实很重要所以又重新再梳理一次。

前言
这段时间突然发现JS原生好多东西都忘记了,但有些东西确实很重要,所以又重新再梳理一次。主要有函数的3种定义方法,ES5函数this指向,call与appl用法,JS常见的4种设计模式,原型链,原型链和继承的方式(ES5和ES6)
1.函数的3种定义方法 1.1 函数声明
//ES5
function getSum(){}
function (){}//匿名函数
//ES6
()=>{}//如果{}内容只有一行{}和return关键字可省,
1.2 函数表达式(函数字面量)
//ES5
var sum=function(){}
//ES6
let sum=()=>{}//如果{}内容只有一行{}和return关键字可省,
1.3 构造函数
var sum=new GetSum(num1,num2)
1.4 三种方法的对比

1.函数声明有预解析,而且函数声明的优先级高于变量;
2.使用Function构造函数定义函数的方式是一个函数表达式,这种方式会导致解析两次代码,影响性能。第一次解析常规的JavaScript代码,第二次解析传入构造函数的字符串

2.ES5中函数的4种调用

在ES5中函数内容的this指向和调用方法有关

2.1 函数调用模式

包括函数名()和匿名函数调用,this指向window

 function getSum() {
    console.log(this) //window
 }
 getSum()
 
 (function() {
    console.log(this) //window
 })()
 
 var getSum=function() {
    console.log(this) //window
 }
 getSum()
2.2 方法调用

对象.方法名(),this指向对象

var objList = {
   name: "methods",
   getSum: function() {
     console.log(this) //objList对象
   }
}
objList.getSum()
2.3 构造器调用

new 构造函数名(),this指向构造函数

function Person() {
  console.log(this); //指向构造函数Person
}
var personOne = new Person();
2.4 间接调用

利用call和apply来实现,this就是call和apply对应的第一个参数,如果不传值或者第一个值为null,undefined时this指向window

function foo() {
   console.log(this);
}
foo.apply("我是apply改变的this值");//我是apply改变的this值
foo.call("我是call改变的this值");//我是call改变的this值
3.ES6中函数的调用

箭头函数不可以当作构造函数使用,也就是不能用new命令实例化一个对象,否则会抛出一个错误
箭头函数的this是和定义时有关和调用无关
调用就是函数调用模式

(() => {
   console.log(this)//window
})()

let arrowFun = () => {
  console.log(this)//window
}
arrowFun()

let arrowObj = {
  arrFun: function() {
   (() => {
     console.log(this)//arrowObj
   })()
   }
 }
 arrowObj.arrFun();
 
4.call,apply和bind

1.IE5之前不支持call和apply,bind是ES5出来的;
2.call和apply可以调用函数,改变this,实现继承和借用别的对象的方法;

4.1 call和apply定义

调用方法,用一个对象替换掉另一个对象(this)
对象.call(新this对象,实参1,实参2,实参3.....)
对象.apply(新this对象,[实参1,实参2,实参3.....])

4.2 call和apply用法

1.间接调用函数,改变作用域的this值
2.劫持其他对象的方法

var foo = {
  name:"张三",
  logName:function(){
    console.log(this.name);
  }
}
var bar={
  name:"李四"
};
foo.logName.call(bar);//李四
实质是call改变了foo的this指向为bar,并调用该函数

3.两个函数实现继承

function Animal(name){   
  this.name = name;   
  this.showName = function(){   
    console.log(this.name);   
  }   
}   
function Cat(name){  
  Animal.call(this, name);  
}    
var cat = new Cat("Black Cat");   
cat.showName(); //Black Cat

4.为类数组(arguments和nodeList)添加数组方法push,pop

(function(){
  Array.prototype.push.call(arguments,"王五");
  console.log(arguments);//["张三","李四","王五"]
})("张三","李四")

5.合并数组

let arr1=[1,2,3]; 
let arr2=[4,5,6]; 
Array.prototype.push.apply(arr1,arr2); //将arr2合并到了arr1中

6.求数组最大值

Math.max.apply(null,arr)

7.判断字符类型

Object.prototype.toString.call({})
4.3 bind

bind是function的一个函数扩展方法,bind以后代码重新绑定了func内部的this指向,不会调用方法,不兼容IE8

var name = "李四"
 var foo = {
   name: "张三",
   logName: function(age) {
   console.log(this.name, age);
   }
 }
 var fooNew = foo.logName;
 var fooNewBind = foo.logName.bind(foo);
 fooNew(10)//李四,10
 fooNewBind(11)//张三,11  因为bind改变了fooNewBind里面的this指向
4.4 call,apply和bind原生实现

call实现:

Function.prototype.newCall = function(context, ...parameter) {
  context.fn = this;  
  context.fn(...parameter);
  delete context.fn;
}
let person = {
  name: "Abiel"
}
function sayHi(age,sex) {
  console.log(this.name, age, sex);
}
sayHi.newCall (person, 25, "男"); // Abiel 25 男

apply实现:

Function.prototype.newApply = function(context, parameter) {
  if (typeof context === "object") {
    context = context || window
  } else {
    context = Object.create(null)
  }
  let fn = Symbol()
  context[fn] = this
  context[fn](parameter);
  delete context[fn]
}

bind实现:

Function.prototype.bind = function (context,...innerArgs) {
  var me = this
  return function (...finnalyArgs) {
    return me.call(context,...innerArgs,...finnalyArgs)
  }
}
let person = {
  name: "Abiel"
}
function sayHi(age,sex) {
  console.log(this.name, age, sex);
}
let personSayHi = sayHi.bind(person, 25)
personSayHi("男")
4.5 三者异同

同:都是改变this指向,都可接收参数
异:bind和call是接收单个参数,apply是接收数组

5.函数的节流和防抖
类型 概念 应用
节流 某个时间段内,只执行一次 滚动条,resize事件一段时间触发一次
防抖 处理函数截止后一段时间依次执行 scroll,resize事件触发完后一段时间触发

节流:

5.1 节流
let throttle = function(func, delay) {
    let timer = null;
    return function() {
      if (!timer) {
        timer = setTimeout(function() {
          func.apply(this, arguments);
          timer = null;
        }, delay);
      }
    };
  };
  function handle() {
    console.log(Math.random());
  }
  window.addEventListener("scroll", throttle(handle, 1000)); //事件处理函数
5.2 防抖
function debounce(fn, wait) {
    var timeout = null;
    return function() {
      if (timeout !== null) clearTimeout(timeout);//如果多次触发将上次记录延迟清除掉
      timeout = setTimeout(function() {
          fn.apply(this, arguments);
          timer = null;
        }, wait);
    };
  }
  // 处理函数
  function handle() {
    console.log(Math.random());
  }
  // 滚动事件
  window.addEventListener("onscroll", debounce(handle, 1000));
6.原型链 6.1 定义

对象继承属性的一个链条

6.2构造函数,实例与原型对象的关系

var Person = function (name) { this.name = name; }//person是构造函数
var o3personTwo = new Person("personTwo")//personTwo是实例

原型对象都有一个默认的constructor属性指向构造函数

6.3 创建实例的方法

1.字面量

let obj={"name":"张三"}

2.Object构造函数创建

let Obj=new Object()
Obj.name="张三"

3.使用工厂模式创建对象

function createPerson(name){
 var o = new Object();
 o.name = name;
 };
 return o; 
}
var person1 = createPerson("张三");

4.使用构造函数创建对象

function Person(name){
 this.name = name;
}
var person1 = new Person("张三");
6.4 new运算符

1.创了一个新对象;
2.this指向构造函数;
3.构造函数有返回,会替换new出来的对象,如果没有就是new出来的对象
4.手动封装一个new运算符

var new2 = function (func) {
    var o = Object.create(func.prototype);    //创建对象
    var k = func.call(o);             //改变this指向,把结果付给k
    if (typeof k === "object") {         //判断k的类型是不是对象
        return k;                  //是,返回k
    } else {
        return o;                  //不是返回返回构造函数的执行结果
    }
}  

更多详情:详谈JavaScript原型链

6.5 对象的原型链

7.继承的方式

JS是一门弱类型动态语言,封装和继承是他的两大特性

7.1原型链继承

将父类的实例作为子类的原型
1.代码实现
定义父类:

// 定义一个动物类
function Animal (name) {
  // 属性
  this.name = name || "Animal";
  // 实例方法
  this.sleep = function(){
    console.log(this.name + "正在睡觉!");
  }
}
// 原型方法
Animal.prototype.eat = function(food) {
  console.log(this.name + "正在吃:" + food);
};

子类:

function Cat(){ 
}
Cat.prototype = new Animal();
Cat.prototype.name = "cat";

// Test Code
var cat = new Cat();
console.log(cat.name);//cat
console.log(cat.eat("fish"));//cat正在吃:fish  undefined
console.log(cat.sleep());//cat正在睡觉! undefined
console.log(cat instanceof Animal); //true 
console.log(cat instanceof Cat); //true

2.优缺点
简单易于实现,但是要想为子类新增属性和方法,必须要在new Animal()这样的语句之后执行,无法实现多继承

7.2 构造继承

实质是利用call来改变Cat中的this指向
1.代码实现
子类:

function Cat(name){
  Animal.call(this);
  this.name = name || "Tom";
}

2.优缺点
可以实现多继承,不能继承原型属性/方法

7.3 实例继承

为父类实例添加新特性,作为子类实例返回
1.代码实现
子类

function Cat(name){
  var instance = new Animal();
  instance.name = name || "Tom";
  return instance;
}

2.优缺点
不限制调用方式,但不能实现多继承

7.4 拷贝继承

将父类的属性和方法拷贝一份到子类中
1.子类:

function Cat(name){
  var animal = new Animal();
  for(var p in animal){
    Cat.prototype[p] = animal[p];
  }
  Cat.prototype.name = name || "Tom";
}

2.优缺点
支持多继承,但是效率低占用内存

7.5 组合继承

通过调用父类构造,继承父类的属性并保留传参的优点,然后通过将父类实例作为子类原型,实现函数复用
1.子类:

function Cat(name){
  Animal.call(this);
  this.name = name || "Tom";
}
Cat.prototype = new Animal();
Cat.prototype.constructor = Cat;
7.6 寄生组合继承
function Cat(name){
  Animal.call(this);
  this.name = name || "Tom";
}
(function(){
  // 创建一个没有实例方法的类
  var Super = function(){};
  Super.prototype = Animal.prototype;
  //将实例作为子类的原型
  Cat.prototype = new Super();
})();
7.7 ES6的extends继承

ES6 的继承机制是先创造父类的实例对象this(所以必须先调用super方法),然后再用子类的构造函数修改this,链接描述

//父类
class Person {
    //constructor是构造方法
    constructor(skin, language) {
        this.skin = skin;
        this.language = language;
    }
    say() {
        console.log("我是父类")
    }
}

//子类
class Chinese extends Person {
    constructor(skin, language, positon) {
        //console.log(this);//报错
        super(skin, language);
        //super();相当于父类的构造函数
        //console.log(this);调用super后得到了this,不报错,this指向子类,相当于调用了父类.prototype.constructor.call(this)
        this.positon = positon;
    }
    aboutMe() {
        console.log(`${this.skin} ${this.language}  ${this.positon}`);
    }
}

//调用只能通过new的方法得到实例,再调用里面的方法
let obj = new Chinese("红色", "中文", "香港");
obj.aboutMe();
obj.say();

更多详情请戳:JS继承的实现方式

8.高阶函数 8.1定义

函数的参数是函数或返回函数

8.2 常见的高阶函数

map,reduce,filter,sort

8.3 柯里化

1.定义:只传递给函数一部分参数来调用它,让它返回一个函数去处理剩下的参数

fn(a,b,c,d)=>fn(a)(b)(c)(d)

2.代码实现:

let currying = function(fn) {
    // args 获取第一个方法内的全部参数
    var args = Array.prototype.slice.call(arguments, 1)
    return function() {
        // 将后面方法里的全部参数和args进行合并
        var newArgs = args.concat(Array.prototype.slice.call(arguments))
        // 把合并后的参数通过apply作为fn的参数并执行
        return fn.apply(this, newArgs)
    }
}
8.4 反柯里化

1.定义:

obj.func(arg1, arg2)=>func(obj, arg1, arg2)

2.代码实现:

Function.prototype.uncurrying = function() {
  var that = this;
  return function() {
    return Function.prototype.call.apply(that, arguments);
  }
};
 
function sayHi () {
  return "Hello " + this.value +" "+[].slice.call(arguments);
}
let sayHiuncurrying=sayHi.uncurrying();
console.log(sayHiuncurrying({value:"world"},"hahaha"));
8.5偏函数

1.定义:指定部分参数来返回一个新的定制函数的形式
2.例子:

function foo(a, b, c) {
  return a + b + c;
}
function func(a, b) {
  return foo(a,b,8);
}
参考文献:

https://www.cnblogs.com/tugen...
https://www.cnblogs.com/humin...
https://www.cnblogs.com/cheng...
https://www.cnblogs.com/cheng...

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/94286.html

相关文章

  • JS修仙一界本源

    摘要:时间一晃就是数月,齐云早已把界基本情况了解了,不过至于三座大山里隐藏的谜团却迟迟没有头绪。它是界本源之一。事实上,根本没有构造函数,或者人人都是构造函数。所以,明白这个界的一界本源,才能在这里畅游天地之间。 自计算机宇宙诞生以来,有很多大神通者在这里开天辟地,开创了很多界,有C、C++、Java等世界,它们彼此相连,其中有一处叫做JavaScript的世界,自被开辟以来吸引了很多修行者...

    I_Am 评论0 收藏0
  • JS设计模式Module(模块)模式、Revealing Module(揭示模块)模式

    摘要:模块模式概念模式最初被定义为一种在传统软件工程中为类提供私有和共有封装的方法。应用将暴露的公有指针指向到私有函数和属性上参考设计模式设计模式系列文章设计模式之工厂模式设计模式之单例模式设计模式之外观模式设计模式之模块模式揭示模块模式 Module(模块)模式 概念 Module模式最初被定义为一种在传统软件工程中为类提供私有和共有封装的方法。 通过这种方式,能够使一个单独的对象拥有共有...

    xavier 评论0 收藏0
  • JS面向对象三【this】 (对象和函数间的关系)

    摘要:实际上就是做了这样一件事情显式的指定是回顾一下隐式模式显示模式观点里所有函数都接受个参数第一个第二个是函数被调用时一定会有这个参数如果你用调用函数就是显式的传递和如果你用语法直接调用函数那就去帮你偷偷的传递。 JS面向对象之三【this】 (对象和函数之间的关系) 上一篇,谈了对象和对象的关系,现在我们谈谈对象和函数的关系 先说结论,也就是观点1 观点1: JS里函数和对象没有关系,J...

    roland_reed 评论0 收藏0
  • 好程序员Web前端培训入门JS基础知识梳理汇总

    摘要:好程序员前端培训入门之基础知识梳理汇总,前端工程师是当前各大企业都比较稀缺的人才,薪资待遇和就业前景都很不错。作用域链的前端,始终是当前执行代码所在环境的变量对象。   好程序员Web前端培训入门之JS基础知识梳理汇总,Web前端工程师是当前各大企业都比较稀缺的人才,薪资待遇和就业前景都很不错。不论是专业还是非专业,有基础亦或是无基础,都想通过学习Web前端实现高薪就业。不过,学习要一...

    int64 评论0 收藏0
  • 好程序员Web前端培训入门JS基础知识梳理汇总

    摘要:好程序员前端培训入门之基础知识梳理汇总,前端工程师是当前各大企业都比较稀缺的人才,薪资待遇和就业前景都很不错。作用域链的前端,始终是当前执行代码所在环境的变量对象。   好程序员Web前端培训入门之JS基础知识梳理汇总,Web前端工程师是当前各大企业都比较稀缺的人才,薪资待遇和就业前景都很不错。不论是专业还是非专业,有基础亦或是无基础,都想通过学习Web前端实现高薪就业。不过,学习要一...

    kviccn 评论0 收藏0

发表评论

0条评论

最新活动
阅读需要支付1元查看
<