函数
函数也是对象,也有自己的原型链,可以使用点语法添加属性或方法;
js中任何的函数对象都是Function构造函数创建的; 包括内置的构造函数
声明函数的方式:
- 具名函数
- 匿名函数
- 本质: 都是调用 new Function( ) 来创建的;
- 普通函数(具名函数)
- 匿名函数
- 箭头函数
- 回调函数
- 构造函数
- 自执行函数(立即执行函数)
- 递归函数
- 函数的this指向和改变方法
函数的形参是形式参数;
调用函数时传入的实参是实际参数;
函数不调用,函数不执行;调用函数的方法有很多,键盘事件,鼠标事件等也属于调用函数;
函数内部,实参的集合是arguments,是一个伪数组;
普通函数
普通函数 (函数的声明式写法 ==>函数提升)
定义函数: function fn() { }
调用函数: fn()
实参形参,一一对应,实参是实际传递过去的参数,实参传递给形参,形参起到占位作用,并在函数体内使用形参;
函数作用域,全局作用域,块级作用域,eval作用域 变量提升,函数提升
匿名函数
匿名函数 (使用function关键字声明一个函数,但未给函数命名,若需要传值,直接将参数写到括号内即可。)
匿名函数可以写成箭头函数,省略了function ;
匿名函数: function () { } ==> ()=>{ }
函数表达式: 将匿名函数赋予一个变量则创建函数表达式 : const fn = function (){ }
匿名函数赋值给一个事件则成为事件处理程序: { getName : function(){xxx} }
(函数的表达式写法 ==> 没有函数提升, 必须先声明再调用)
匿名函数赋值给一个变量来保存.
定义函数: const fn = function () { }
调用函数: fn()
箭头函数:
箭头函数: 匿名函数的省略写法,没有自己的this;
定义函数: const fn = () => { }
调用函数: fn() 箭头函数中出现的this实际为它所在上下文中的this;
箭头函数中的this实际上是向外层一级一级的查找作用域中的this(直到this有定义)
省略写法: 当形参只有一个时,可以省略(), 当函数体内只有返回值且只有一条代码时,可以省略 { } 和 return 。
const sum = n => n+'9' ; sum(99) // 999
立即执行函数:
立即执行函数 声明函数后,立即调用函数; IIFE(立即调用函数表达式)
(function (){} ())
(function (){})()
匿名函数 + 立即执行函数表达式 立即执行函数通常是匿名函数;
(function (num) { console.log(num) }(123));
(function (num) { console.log(num) } )(123)
立即执行函数的作用: 立即执行函数会形成一个单独的作用域,可以封装一些临时变量或者局部变量,避免污染全局变量。
1.创建一个独立的作用域,这个作用域里面的变量,外面访问不到,这样就可以避免变量污染。
2.闭包和私有数据。
构造函数
构造函数 : 构造对象的函数,并通过new关键字调用; ES6中class类
构造函数: 够造对象的函数; 对象的实例化:创建够造对象的过程;
构造函数名首字母大写, 通过new关键字调用;
通过this添加对象的属性和方法;
构造函数的是通过new调用的,this指向的是调用它的实例对象==>this指向构造的这个对象;
'use strict'模式下,this就为undefined。
构造函数不需要return;构造函数会创建一个新对象,并将该新对象作为返回值返回;
如果出现了return,返回的如果是值类型,则忽略return ;
如果出现了return,返回的如果是引用类型 则以return为准;
new 做的事情;
- 在内存中创建一个新的空对象;
- 让this与这个空对象绑定; 让空对象的__proto__指向构造函数的prototype,继承了该函数的原型。(将构造函数的原型对象赋值到对象的原型链上)
- 执行函数体内的代码;
- 返回这个新对象(不需要return);
function Person(a,b){ this.name =a ; this.age = b; this.sing = function(gequ){ console.log(gequ); console.log(this);// 构造函数中的this ==> 调用它的实例对象 Person {name: '李华', age: 18, sing: ƒ} } console.log(this); // 构造函数中的this ==> 调用它的实例对象 Person {name: '李华', age: 18, sing: ƒ} } const obj = new Person("李华",18); obj.sing("十年") ;//调用构造函数中的方法 console.log(typeof obj); //类型 object console.log(obj); // 实例对象 Person {name: '李华', age: 18, sing: ƒ}
回调函数
回调,就是回头调用的意思。主函数的事先干完,回头再调用传进来的那个函数。
回调: 回头调用;
异步async,同步sync;
异步回调,同步回调;
JS/Node是单线程的,从上往下一次解析代码,执行代码;
回调函数:一个函数作为参数传递给另一个函数并在其父函数完成后执行的函数。
回调是一个函数的调用过程。
函数a有一个参数,这个参数是个函数b,当函数a执行完以后执行函数b,那么这个过程就叫回调。
函数b是你以参数形式传给函数a的,那么函数b就叫回调函数; 回调,回调,就是回头调用的意思,主函数的事先干完,回头再调用传进来的那个函数。
// 主函数A function A(callBack) { callBack(); console.log("主函数"); } // 回调函数B function B(){ setTimeout(() => { console.log("回调函数"); }, 1000); } A(B) // 调用主函数A, 回调函数B作为参数传入; 结果: 主函数, 回点函数;
递归函数
1. 与循环类似;
2. 要有结束条件,防止死循环;
/ 单函数递归 / 单函数递归: 自己调用自己; function fn() { fn() } fn() / 双函数递归 / 双函数递归: 两个函数互相调用; function fn1() { fn2() } function fn2() { fn1() } 调用fn1()或fn2() / 递归求和/ *1. function getSum(n) { return n == 1 ? 1 : n + getSum(n - 1); } getSum(5) //15 *2. function getSum(n) { if (n === 1) { // 结束条件 return 1; } else { return n + getSum(n - 1); } } getSum(5) //15
this指向
普通函数, 箭头函数, 对方中的方法, 构造函数, 事件函数;
函数的this指向: 谁调用它,指向谁;
函数调用的三种方法,
- this指向 普通函数: 函数名() this指向windown; (全局作用域下);
- 对象中的方法: 对象名.方法名() this指向这个对象;
- 构造函数: new 函数名() this指向创建的实例对象;
- 箭头函数: 没有this, 它的this是所在作用域中的this指向,外侧也没有,直到windown上;
- 定时器中的普通函数this默认指向windown; 事件函数的this指向触发者;
改变函数的this指向
函数上下文调用模式; 修改this指向;
this只能是引用数据类型,如果不是,会自定转成对应的引用数据类型;
thi如果是null,undefined,修改this无效,还是原来的this指向;
改变函数this指向的方法 call , apply , bind;
- 函数名.call(新的this指向, 参数1,参数2,...), 适用于参数固定;
- 函数名.apply(新的this指向,[参数1,参数2,...]),适用于多个参数,参数不固定;
- 函数名.bind(新的this指向,参数1,参数2,...)() ,适用于定时器函数,事件处理函数;
bind不会立即指向函数,会得到一个改变this指向后的函数,调用这个新函数来执行函数;
bind改变函数this指向后,传参,参数将会固定,可以调用新函数时传参;
区别:
- 传参不一样, 执行机制不一样;
- call 和 applay 都会立即调用函数改变它的this指向;
- bind不会调用函数,可以改变函数的指向,bind会返回一个新函数(改变this之后产生的新函数),调用这个新函数来改变this指向;
- call传参,依次传参;
apply传参,需要把参数放到一个数组或伪数组中;
bind传参,依次传参; // IIFE ()()
/ 全局作用域下的普通函数 / const obj = {name:"张鸭蛋"}; function fn(a,b){ console.log(a,b); // undefined undefined console.log(this); // window } fn() / call()方法 / const obj = {name:"张鸭蛋"}; function fn(a,b){ console.log(a,b); // 1 2 console.log(this); // 指向obj对象 {name: '张鸭蛋'} } fn.call(obj,1,2) / apply()方法 / const obj = {name:"张鸭蛋"}; function fn(a,b){ console.log(a,b); // hello world console.log(this); // 指向obj对象 {name: '张鸭蛋'} } fn.apply(obj,["hello","world"]) // 参数放到一个数组/伪数组中,会遍历这个数组进行传参; / bind() 方法 / const obj = {name:"张鸭蛋"}; function fn(a,b){ console.log(a,b); // 你好 世界 console.log(this); // 指向obj对象 {name: '张鸭蛋'} } fn.bind(obj,"你好","世界")(); // 传递参数固定了, 后续再次传参无效; fn.bind(obj)("你好","世界") const fn2 = fn.bind(obj); // 返回一个新的函数,再次调用这个新函数并传参; fn2("你好","世界")