JS 高级
继承
构造函数继承
<script>
// 构造函数继承
// 定义的Person类
function Person(name, age) {
this.name = name;
this.age = age;
}
// 定义的Student类
function Student(name, age) {
// 继承
// 借用构造函数继承属性成员
Person.call(this, name, age);
}
var stu = new Student("老王", 30);
console.log(stu.name); // 老王
console.log(stu.age); // 30
</script>
拷贝继承(for - in)
<script>
// copy 继承
// 定义的Person类
function Person(name, age) {
this.name = name;
this.age = age;
}
// 用 prototype 给 Person类 添加新的属性方法
Person.prototype.sayHi = function () {
console.log(`${this.name}:'Hi'`);
};
// 定义的Student类
function Student(name, age) {
// 继承属性
Person.call(this, name, age);
}
// copy 继承,继承原型的属性
for (var key in Person.prototype) {
Student.prototype[key] = Person.prototype[key];
}
var stu = new Student("张三", 20);
console.log(stu.name); // 张三
console.log(stu.age); // 20
stu.sayHi();
</script>
另一种继承方式:原型继承
<script>
// 定义的Person类
function Person(name, age) {
this.name = name;
this.age = age;
}
// 用 prototype 给 Person类 添加新的属性方法
Person.prototype.sayHi = function () {
console.log(`${this.name}:'Hi'`);
};
// 定义的Student类
function Student(name, age) {
// 继承属性
Person.call(this, name, age);
}
// 将 Person 对象赋值给 Student 的原型
// Student.prototype = new Person(); // new Person() Person
Student.prototype = Person.prototype; // Person.prototype Object
var stu = new Student("张三", 20);
console.log(stu);
stu.sayHi();
</script>
函数
函数进阶 - 函数的定义方式
-
函数声明
-
// 函数的定义方式:声明式 function foo() {}
-
-
函数表达式
-
// 字面量方式 :表达式 var foo = function () {};
-
-
函数声明与函数表达式的区别
-
函数声明必须要有名字
-
函数声明会函数提升,在预解析创建就已创建,声明前后都可以调用
-
函数表达式类似于变量赋值
-
函数表达式可以没有名字,例如匿名函数
-
函数表达式没有变量提升,在执行阶段创建,必须在表达式执行之后才可以调用
<script> // 函数的定义方式:声明式 变量提升 function foo() {} // 字面量方式 :表达式 // 没有变量提升 // foo() 会报错 var foo = function () {}; </script>
-
函数进阶 - 函数的调用方式
- 普通函数
- 构造函数
- 对象方法
<script>
// 普通函数
function foo() {}
foo();
// 构造函数
function F() {}
var a = new F();
// 对象的函数
var c = {
sayHi: function () {},
};
c.sayHi();
</script>
函数进阶 - 函数内 this 指向的不同场景
-
函数的调用方式决定了 “this” 指向的不同:
调用方式 非严格模式 备注 普通函数调用 window 严格模式下是 undefined 构造函数调用 实例对象 原型方法中 this 也是实例对象 对象方法调用 该方法所属对象 紧挨着的对象 事件绑定方法 绑定事件对象 定时器函数 window
<body>
<button id="btn">按钮</button>
</body>
<script>
// 普通函数
function foo() {
console.log(this); // window
}
foo();
// 构造函数
function Person() {
console.log(this); // 指向实例对象 Person
}
var p = new Person();
var obj = {
name: "张三",
age: 30,
sayHi: function () {
console.log(this); // obj 张三
},
grilFriend: {
name: "小红",
talk: function () {
console.log(this); // grilFriend 小红
},
},
};
// console.log(obj);
obj.sayHi();
obj.grilFriend.talk();
document.getElementById("btn").onclick = function () {
console.log(this); // <button id="btn">按钮</button>
};
// 定时器函数
setTimeout(function () {
console.log(this); // window
}, 2000);
</script>
函数进阶 - call、apply、bind
- 了解了函数 this 指向的不同场景之后
- 我们知道有些情况下我们为了使用某种特定环境的 this 引用,这时候我们就需要采用一些特殊手段来处理了,例如我们经常在定时器外部备份 this 引用,然后在定时器函数内部使用外部 this 的引用
- 然而实际上对于这种做法我们的 JavaScript 为我们专门提供了一些函数方法用来帮我们更优雅的处理函数内部 this 指向问题。
<script>
// 更改 this 指向
var obj = {
name: "张三",
sayHi: function () {
console.log(this); // obj
var _this = this;
setTimeout(function () {
console.log(this); // window
console.log(name); // ''
console.log(this.name); // ''
console.log(_this.name); // 张三
}, 2000);
},
};
obj.sayHi();
</script>
call
call()
方法调用一个函数,其具有一个指定的this
值和分别提供的参数(参数的列表)。- 注意:该方法的作用和
apply()
方法类似,只有一个区别,就是call()
方法接受的是若干个参数的列表,而apply()
方法接受的是一个包含多个参数的数组。 - 语法:
fun.call(thisArg,arg1,arg2)
- 参数:
-
thisArg
- 在 fun 函数运行时指定的 this 值
- 如果指定了 null 或者 undefined 则内部 this 指向 window
-
arg1,arg2,...
- 指定的参数列表
-
<script>
var obj = {
name: "admin",
};
function foo(a, b) {
console.log(a + b); // 30
console.log(this.name); // admin this指向了obj
}
foo.call(obj, 10, 20);
</script>
apply
-
apply()
方法调用一个函数,其具有一个指定的this
值,以及作为一个数组(或类似数组的对象)提供的参数 -
注意:该方法的作用和
call()
方法类型,只有一个区别,就是call()
方法接受的是若干个参数的列表,而apply()
方法接受的是一个包含多个参数的数组 -
语法:
fun.apply(thisArg,[argsArray])
-
参数:
thisArg
argsArray
-
apply()
与call()
非常相似,不同之处在于提供参数的方式 -
apply()
使用参数数组而不是一组参数列表 -
例如:
fun.apply(this,['eat','bananas'])
<script> var obj = { name: "admin", }; function foo(a, b) { console.log(a + b); // 30 console.log(this.name); // admin this指向了obj } foo.apply(obj, [10, 20]); // call 和 apply 很相似,只是在传递参数的时候一个是列表,一个是数组 </script>
bind
-
bind()
函数会创建一个新函数(称为绑定函数),新函数与被调函数(绑定函数的目标函数)具有相同的函数体 -
当目标函数被调用时
this
值绑定到bind()
的第一个参数,该参数不能被重新。绑定函数被调用时,bind()
也接受预设的参数提供给原函数 -
一个绑定函数也能使用 new 操作符创建对象:这种行为就像把原函数当成构造器。提供的
this
值被忽略,同时调用时的参数被提供给模拟函数 -
语法:
fun.bind(thisArg,arg1,arg2)()
-
参数:
thisAry
- 当绑定函数被调用时,该参数会作为原函数运行时的
this
指向。当使用 new 操作符调用绑定函数时,该参数无效
- 当绑定函数被调用时,该参数会作为原函数运行时的
arg1,arg2,...
- 当绑定函数被调用时,这些参数将置于实参之前传递给被绑定的方法
<script> var obj = { name: "admin", }; function foo(a, b, c, d) { console.log(a + b); // 30 console.log(this.name); // admin this指向了obj console.log(c, d); // 30 40 } // 最后一个括号表示调用 // 会将两个括号中的参数进行合并 全部传递 foo.bind(obj, 10, 20)(30, 40); </script>
小结:
-
call 和 apply 特性一样
- 都是用来调用函数,而且是立即调用
- 但是可以在调用函数的同时,通过第一个参数指定函数内部
this
的指向 - call 调用的时候,参数必须是以参数列表的形式进行传递,也就是逗号分割的方式依次传递即可
- apply 调用的时候,参数必须是一个数组,然后在执行的时候,会将数组内部的元素一个一个拿出来,与形参一一对应进行传递
- 如果一个参数指定了 null 或者 undefined 则内部 this 指向 window
-
bind
- 可以用来指定内部 this 的指向,然后生成一个改变了 this 指向的新的函数
- 它和 call、apply 最大的区别是:bind 不会调用
- bind 支持传递参数,它的传递方式比较特殊,一个有两个位置可以传递
- 在 bind 的同时,以参数列表的形式进行传递
- 在调用的时候,以参数列表的形式进行传递
- bind 传递参数的时候以传递的参数为准还是以调用的时候传递的参数为准
- bind 的时候传递的参数和调用的时候的参数会合并到一起,传递到函数内部
函数的其他成员
- arguments
- 实参集合
- length
- 形参的个数
- name
- 函数的名称
<script>
function a() {
console.log(arguments); // 参数列表 10 20 30 40 是一个伪数组
}
a(10, 20, 30, 40);
// 函数形参的个数
console.log(a.length); // 4
console.log(a.name); // a
var foo = "FOO";
var obj = {
[foo]: function () {
console.log("123456");
},
};
obj[foo]();
console.log(obj[foo].name); // FOO
</script>
高级函数
- 函数可以作为参数
- 函数可以作为返回值
<script>
// 高阶函数
// 第一种:参数是一个函数 map filter reduce forEach
var a = [1, 2, 3];
var b = a.map(function (item, index, arr) {
return item * 2;
});
console.log(b);
var c = a.filter(function (item, index, arr) {
return item == 3;
});
console.log(c);
var d = a.reduce(function (pre, item, index, arr) {
return (pre += item);
}, 0);
console.log(d);
a.forEach(function (item, index, arr) {
console.log(item);
});
// 第二种高阶函数:返回值是一个函数
function foo() {
return function () {
console.log(10);
};
}
// var f = foo();
// f();
foo()();
</script>
函数闭包
- 一个函数对其周围状态的引用捆绑在一起(或者说函数被引用包围),这样的组合就是闭包(closure)
- 也就是说,闭包让你可以在一个内存函数中访问到其外层函数的作用域。在 JavaScript 中,每当创建一个函数,闭包就会在函数创建的同时被创建出来
<script>
function foo() {
var counter = 10;
return {
printCounter: function () {
console.log(counter);
},
};
}
// 调用函数 返回一个对象
var obj = foo();
// 对象调方法
obj.printCounter();
// 链式调用
foo().printCounter();
</script>
作用域
- 全局作用域
- 函数作用域
- 没有块级作用域
<script>
// 代码块
{
var a = 10; // 全局作用域
}
console.log(a); // 10
// function foo() {
// console.log(a);
// }
// foo();
(function foo() {
console.log(a); // 10
})();
</script>
作用域链
- 内层作用域可以访问外层作用域,反之不行
<script>
var a = 10;
function fn() {
var b = 20;
function fn1() {
var c = 30;
console.log(a + b + c); // 60
}
function fn2() {
var d = 40;
console.log(c + d); // 报错
}
fn1();
fn2();
}
fn();
</script>