1.普通函数
-
普通函数
-
不管是不是全局作用域,this都指向执行环境或者调用对象(重点理解)
// 1.如果在html文件中执行,那么执行环境为window
<html lang="en">
// 在html文件中引入script文件
<script src="./test.js"></script>
// 在html文件中直接使用script标签执行JS代码
<script>
function myfn(){
console.log(this); // this指向window对象
}
myfn();
console.log(this); // this指向window对象
</script>
</html>
// test.js文件下的代码
function myfn(){
console.log(this); // window对象
}
// 调用方式
myfn();
// 或者 利用双阔号的方式,让其自动调用(网页被打开或者测试的时候,它就自行调用)
(function myfn(){
console.log(this);
})()
// 2.在JS文件下测试(node环境),执行环境是准确来讲应该是 module.exports,也就是{} 空对象,也就会出现undefined(重点理解)
function myfn(){
console.log(this); // undefined
}
myfn();
console.log(this); // undefined
-
注意:
-
在严格模式下(函数内部的this不能指向全局对象,默认等于undefined)
-
全局作用域下,this指向执行环境
-
非全局作用域下,this指向undefined
-
// 1.在html文件下执行
<body>
<script>
"use strict"; // 严格模式下
function myfn() {
console.log(this); // 非全局作用域里面的this指向 undefined
}
myfn()
console.log(this); // 全局作用域里面this指向执行环境 window
</script>
</body>
// 2.在JS文件下测试,没有对应的执行环境,则不管是不是全局作用域,this都指向 node环境的module.exports空对象{}
"use strict"; // 严格模式下
function myfn() {
console.log(this); // undefined
}
myfn() // 调用函数
console.log(this); // undefined
-
匿名函数
-
说白了就是用一个变量来接收函数的返回值,输出这个变量,也就是输出这个变量的返回值
-
-
注意:
-
竟然是用变量来接收一个函数的返回值,则这个函数的性质就跟着定义变量的关键字来了,也就是说变量提不提升(意味着函数提不提升),提升之后是否进行初始化啊等等这些问题
-
let、const、var都能造成变量提升(函数提升),但是let、const不初始化函数,而var初始化函数,输出 undefined
-
this指向执行环境或者调用对象(重点理解)
// 语法格式:
let myfn = function(){ // 接收变量为 myfn
console.log(this); // this指向执行环境
}
myfn() // 这里是执行环境调用的 myfn()
let obj = {
sing:function(){ // 接收的变量为 sing
console.log(this); // this指向 obj对象
}
}
obj.sing() // 这里是 obj 对象调用的 sing
// var 变量提升
console.log(myfn) // 这里面myfn后面不能接括号,因为myfn这个时候被初始化成 undefined了,如果加括号会报错 myfn不是一个函数
var myfn = function(){ // var有变量提升,且初始化变量,myfn
console.log(this)
};
// let、const 变量提升,但是不会初始化变量,则会直接报错 myfn没有被初始化
console.log(myfn)
const myfn = function(){
console.log(this)
}
console.log(myfn)
let myfn = function(){
console.log(this)
}
-
构造函数(重点理解)
-
定义
-
构造函数是一种特殊的函数,主要用来初始化对象,即为对象成员变量赋初始值,它总与 new 一起使用
-
-
创建(区别普通函数)
-
首字母一般大写
-
函数体内部使用了this关键字,代表了所要生成的对象实例(最后返回的也就是这个this实例对象)
-
生成对象的时候,必须使用new命令
-
-
运用
-
使用new关键字运用,另外可以用一个变量接收
-
-
注意:(特别注意)
-
构造函数必须使用new关键词调用,不然会出现变量外出的误会
-
当利用new关键字时,构造函数内如果出现return语句返回一个数值,new命令则会忽略这个数值,返回“构造”后的this对象
-
当利用new关键字时,构造函数内如果出现return语句返回一个与this无关的对象,new命令则会返回这个新对象,而不是“构造”的this对象
-
-
为了保证构造函数必须与new命令一起使用,一个解决办法是,构造函数内部使用严格模式,即构造函数内部第一行加上“use strict”(严格模式)。这样的话,一旦忘了使用new命令,直接调用构造函数就会报错。
// 创建构造函数方法(1)
function Start(name,age){
// 构造函数里面的属性
this.name = name;
this.age = age;
// 构造函数下的方法
this.sing = function(){
console.log('我会唱歌')
}
}
// 创建构造函数方法(2)
let Start = function(name,age){
// 构造函数里面的属性
this.name = name;
this.age = age;
// 构造函数下的方法
this.sing = function(){
console.log('我会唱歌')
}
}
// 运用构造函数
let start = new Start('ldh',23);
// 调用构造函数里面的方法
start.sing()
// 未用 new 关键字调用 构造函数(造成误会)
function Myfn(){
this.height = 190;
return 180;
}
let myfn = Myfn();
console.log(myfn); // 180
console.log(window.height); // 190
// 利用 new 关键字调用 构造函数 (含有return返回数值)
function Myfn(){
this.height = 190;
return 190; // 有return 语句,并且返回的是一个数值,则会被new忽略
}
let myfn = new Myfn();
console.log(myfn); // 得到Myfn {height: 190},而不是return返回来的值,说明new使 自定义的return返回的数值被忽略了
console.log(window.height); // undefined
// 利用 new 关键字调用 构造函数 (含有return返回新的对象)
function Myfn(){
this.height = 190;
return {height : 180,price : 20}; // 有return 语句,并且返回的是一个新的对象,则新对象会把“构造”的this对象覆盖
}
let myfn = new Myfn();
console.log(myfn); // 得到Myfn {height : 180,price : 20},而不是return返回来的值,说明new使 自定义的return返回的数值被忽略了
console.log(window.height); // undefined
1.new关键字干了些什么?(重点理解)
-
创建一个空对象,作为将要返回的对象实例。
-
将这个空对象的原型(__proto__)指向构造函数的prototype属性。
-
将这个空对象赋值给函数内部的this关键字。
-
开始执行构造函数内部的代码。
也就是说,构造函数内部,this指的是一个新生成的空对象, 所有针对this的操作,都会发生在这个空对象上。构造函数之所以叫“构造函数”, 就是说这个函数的目的,就是操作一个空对象(即this对象),将其“构造”为需要的样子!
2.new.target是干嘛的?(重点理解)
用来指向new关键字处理的函数,如果不为new关键字处理,则new.target为undefined
用途:可以用来判定函数是否由new关键字处理
function Myfn(name, age) {
this.name = name;
this.age = age;
if(new.target === undefined){
throw Error('请使用new');
}else{
console.log('你成功使用了new');
}
}
let myfn = new Myfn('ldh', 23); // '你成功使用了new'
let myfn = Myfn('ldh',23); // '请使用new'
重点理解:
1.对象是什么?(记忆理解)
面向对象编程(Object Oriented Programming,缩写为 OOP)是目前主流的编程范式。它将真实世界各种复杂的关系,抽象为一个个对象,然后由对象之间的分工与合作,完成对真实世界的模拟
(1)对象是单个实物的抽象。
一本书、一辆汽车、一个人都可以是对象,一个数据库、一张网页、一个与远程服务器的连接也可以是对象。当实物被抽象成对象,实物之间的关系就变成了对象之间的关系,从而就可以模拟现实情况,针对对象进行编程。
(2)对象是一个容器,封装了属性(property)和方法(method)。
属性是对象的状态,方法是对象的行为(完成某种任务)。比如,我们可以把动物抽象为animal对象,使用“属性”记录具体是那一种动物,使用“方法”表示动物的某种行为(奔跑、捕猎、休息)等等。
2.构造函数和普通函数的对比
区别:构造函数是一个封装对象,可以有属性和方法并且可以使用new关键字得到一个实例对象,之后通过这个实例对象来运用它们,而普通函数只能处理简单逻辑,得到某个值
// 构造函数
function Myfn(name){
// 属性
this.name = name;
// 方法
this.sing = function(){
console.log('我会唱歌')
}
}
let myfn = new Myfn(); // 得到一个 Myfn{name,sing(){console.log('我会唱歌')}} 对象,
// 我们可以根据new出来的实例运用对象里的方法和属性
console.log(myfn.sing); // '我会唱歌'
// 普通函数
function myfn(){
let name = 'ldh';
let sing = function(){
console.log('我会唱歌');
}
return myfn; // 得到的是一个 myfn对象值,而并不是对象
}
let fn = myfn(); // 这里就是 fn 变量接受了 myfn() 函数的return的值,也就是 myfn对象值
/*
// myfn对象值
ƒ func(){
let a = 10;
let sing = function(){
console.log(this);
}
return func;
}
*/
console.log(fn.sing); // undefined
3.Arguments对象
arguments是一个对应于传递给函数的参数的类数组对象。除了箭头函数以外的所有函数都有这个局部变量,它存储着我们手动传入的参数。
// 普通函数
function myfn(n1,n2){
console.log(arguments); // 得到一个类数组 Arguments(2) [1, 2, callee: ƒ, Symbol(Symbol.iterator): ƒ] 存下了我们传进的参数 1,2
console.log(arguments[0]); // 1
console.log(arguments[1]); // 2
}
myfn(1,2);
// 构造函数
function Myfn(name,age){
this.name = name;
this.age = age;
console.log(arguments); // 得到一个类数组 Arguments(2) ['ldh', 23, callee: ƒ, Symbol(Symbol.iterator): ƒ]
console.log(arguments[0]); // 'ldh'
console.log(arguments[1]); // 23
}
let myfn = new Myfn('ldh',23);
2.箭头函数
-
与普通函数的区别
在ES6中,箭头函数是其中最有趣的新增特性之一,箭头函数是一种使用箭头 => 定义函数的新语法,但它和传统的JavaScript函数有些许不同:
1.没有this、super、arguments和new.target绑定:(以下三种情况)
-
1.1对象字面量里面含有箭头函数
-
箭头函数中的this、super、arguments和new.target这些值由外围最近一层非箭头函数(离得最近的function字样)所决定。其实再说简单点就是箭头函数的this在定义它的时候就诞生了,且this的指向是外围的第一个含有function字样的对象的作用域对应的变量值
-
-
1.2构造函数里面包含箭头函数
-
箭头函数中的this、super、arguments和new.target这些值由构造函数决定,也就是this指向构造函数
-
-
1.3普通函数里面包含箭头函数
-
箭头函数中的this、super、arguments和new.target这些值由执行环境决定,也就是this指向执行环境变量
-
2.不能通过new关键词调用:因为箭头函数没有[[Construct]]函数,所以不能通过new关键词进行调用,如果使用new进行调用会抛出错误。
3.没有原型:因为不会通过new关键词进行调用,所以没有构建原型的需要,也就没有了prototype这个属性。
4.不可以改变this的绑定:在箭头函数的内部,this的值不可改变(即不能通过call、apply或者bind等方法来改变)。
5.不支持arguments对象:箭头函数没有arguments绑定,所以必须使用命名参数或者不定参数这两种形式访问参数。
6.不支持重复的命名参数:无论是否处于严格模式,箭头函数都不支持重复的命名参数。
-
箭头函数的this问题(重点理解)
-
对象字面量中包含箭头函数的情况(重点理解)
-
// 1.普通函数的this指向和改变this指向
var name = 'window'; // 其实是window.name = 'window'
var A = {
name: 'A',
sayHello: function(){
console.log(this.name)
}
}
A.sayHello();// 输出A (普通函数的this指向,谁调用它,它就指向谁)
var B = {
name: 'B'
}
A.sayHello.call(B);//输出B (普通函数的this指向可以被改变)
A.sayHello.call();//不传参数指向全局window对象,输出window.name也就是window
// 2.箭头函数的this指向以及this指向能否改变
var name = 'window';
var A = {
name: 'A',
// 定义sayHello为箭头函数,它的this会往外围寻找,也就是先找到 var A = {} 块级作用域,但并不含有function字样,则继续往外围找,找到最后都没找到
// this就会指向执行环境对应的对象(浏览器对应window,node对应module.exports空对象{}[undefined])
sayHello: () => {
console.log(this.name)
}
}
A.sayHello();// 还是以为输出A ? 错啦,其实输出的是window
// 3.再举个例子(在浏览器环境下执行)
let obj = {
sing: () => {
console.log(this); // 往上找function字样,找不到,就指向执行环境对应的对象,所以指向window
let ring = () => {
console.log(this); // 往上找function字样,找不到,就指向执行环境对应的对象,所以指向window
}
return ring();
}
}
let obj1 = {};
obj.sing();
obj.sing.apply(obj1); // 上面的指向依旧不改变,还是指向window
// 4.继续举个例子(在浏览器环境下执行)
let obj = {
sing:function(){
console.log(this); // 普通函数,谁调用,this就指向谁,则指向obj
let ring = () => {
console.log(this); // 往上找function字样,找到sing是带有function字样的,sing所在obj的块级作用域中,则就指向obj这个变量
}
return ring();
}
}
let obj1 = {};
obj.sing();
obj.sing.apply(obj1); // 可以改变普通函数this指向,则箭头函数跟随function字样的作用域的this
- 普通函数和构造函数里面包含箭头函数(重点理解)
1.构造函数里面含有箭头函数
this会指向构造函数
2.普通函数里面含有箭头函数(用不用闭包都是一样的)
this会指向执行环境变量
// 构造函数里面含有箭头函数
function Myfn(name,age){
this.name = name
this.age = age
this.song = () => {
console.log(this) // 指向构造函数 Myfn {name: 'ldh', age: 23, song: ƒ}
}
}
let myfn = new Myfn('ldh',23);
myfn.song();
// 普通函数里面含有箭头函数(未使用闭包,利用立即调用函数处理)(执行环境为window)
function my(){
let fn = (function(){
let func = (() => {
console.log(this); // 指向window,这里会发现 this 并没有指向 fn ,而是指向window
})();
})();
}
my()
// 普通函数里面含有箭头函数(使用闭包,利用立即调用函数处理)(执行环境为window)
function my(){
let fun = function(){
let func = () => {
console.log(this) // 指向window,这里会发现 this 并没有指向 fun ,而是指向window
}
return func();
}
return fun()
}
my()
-
箭头函数的表现形式(四种)
// 表现形式之一:没有参数
let reflect = () => 5
// 相当于
let reflect = function () {
return 5
}
// 表现形式之二:返回单一值
let reflect = value => value // 单个参数时,可以不用小括号
// 相当于
let reflect = function (value) {
return value
}
// 表现形式之三:多个参数
let reflect = (val1, val2) => val1 + val2 // 没有大括号包裹时,假定是有return的
// 或者
let reflect = (val, val2) => {
return val1 + val2
}
// 相当于
let reflect = function (val1, val2) {
return val1 + val2
}
// 表现形式之四:返回一个对象,对象必须被小括号包裹着才能返回,或者直接使用return
let reflect = (id) => ({ id: id, name: 'why' })
// 相当于
let reflect = function (id) {
return {
id: id,
name: 'why'
}
}