闭包和继承
js有 两种作用域:全局 局部
全局:可以在任意位置使用,且一直会存在在内存中,不会被js垃圾回收机制,回收
局部:函数内部声明的变量,只能在函数内部使用,且在函数调用结束后,会立即销毁,不会保存在内存中
如何在外部访问js函数内部声明的变量
闭包:可以实现在外部访问函数内部声明的变量
结构:函数嵌套函数
好处:
var scope = "global scope";
function checkScope() { //外层函数
var scope = "local scope"; //函数内部声明的变量
function f() {
return scope;
}
return f();
}
checkScope(); ?? //scope
- 什么是闭包
//全局作用域
function a() {
//a的作用域
var i = 0; //不能被销毁
function b() {
//b的作用域
alert(++i);
}
return b;
}
var c = a();
c();
当函数a的内部函数b被函数a外的一个变量引用的时候,就创建了一个闭包。(避免全局变量污染)
- 闭包的特点
- 函数嵌套函数
- 内部函数可以使用外部函数的内部变量
- 函数中的局部变量在外部是不能被引用(保护函数内的变量安全)
- 为什么要写闭包
- 避免全局变量污染
- 将函数内部变量的值始终保存在内存中(在内存中维持一个变量)
- 通过保护变量的安全实现JS私有属性和私有方法(不能被外部访问)
-
全局变量污染
var a = 1 function fn(){ a++; console.log(a) } fn(); fn(); function fn(){ var a = 1; a++; console.log(a) } fh() fn()
-
垃圾回收机制
function abc(){ var str = 'vv' } abc() //使用完str被回收
- 参数和内部变量不会被回收
- 闭包的应用场景
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
</head>
<body>
<button>Button0</button>
<button>Button1</button>
<button>Button2</button>
<button>Button3</button>
<button>Button4</button>
</body>
</html>
<script>
var btns = document.getElementsByTagName('button');
for(var i = 0, len = btns.length; i < len; i++) {
btns[i].onclick = function() {
alert(i);
}
}
</script>
for(var i = 0, len = btns.length; i < len; i++) {
(function(i) {
btns[i].onclick = function() {
alert(i);
}
}(i))
}
自执行函数 (函数声明 和 函数表达式)
var counter = (function(){
var privateCounter = 0; //私有变量
function change(val){ //私有函数
privateCounter += val;
}
return {
increment:function(){ //三个闭包共享一个词法环境
change(1);
},
decrement:function(){
change(-1);
},
value:function(){
return privateCounter;
}
};
})();
es6继承
es5继承通过构造函数结合原型
而es6中新增类的概念
类:所有的对象的 抽象
js中没有类,通过构造函数形式来实现继承 ,构造函数类似于其他语言中的类
注意:es6中的类 只是一个语法糖,构造函数的另一种写法
es6 class用法:
class Person{
constructor(name,sex){
//方法 内部的一个函数 ,自动调用 用于定义属性
this.name = name;
this.sex = sex;
}
act(){
console.log(this.name+"爱睡觉");
}
}
拓展:es6中的对象
var a=10;
var aa = "name";
var obj={
act:function(){},
act(){
},
a:a,
[aa]:"小明"
}
class和es5构造函数
class
class Person {
constructor(name, sex) {
this.name = name;
this.sex = sex;
}
act() {
console.log(this.name + "爱睡觉");
}
}
es5
function Person(name,sex){
this.name = name;
this.sex = sex;
}
Person.prototype.act=function(){
console.log( this.name+"爱睡觉" );
}
注意:
1,es6中的class 只是es5构造函数和原型的 语法糖
2,使用方法相同的 都是通过new
3,class Person 等价于 function Person
4,class中的方法 默认是 添加到 原型上的
5,
contrustor 方法是类 默认的方法,会在new 一个对象 自动调用
如果你没有写constructor 不报错,代码会自动添加一个
class P{
act(){
console.log("你好");
}
}
//自动变成这样
class P{
contructor(){
}
act(){
console.log("你好");
}
}
属性在哪里定义:在constructor中 不管是 constructor 还是自己在class中定义的方法,都是挂载在构造函数的 原型上 这些方法的this指向是什么 实例对象
6,类名必须大写
继承
es5中 实例对象只能继承构造函数
es6中 增加了 父类概念 另一个类 就可以作为这个父类 的 子类 来继承这个父类
class Cat{
// 父类
constructor(cls,feature){
this.cls = cls; //猫类
this.feature = feature;
}
act(){
console.log(this.cls + "打呼噜");
}
act2(){
console.log( this.cls + "埋粑粑" );
}
}
class Buou extends Cat{
constructor(name,fea1,fea2,cls,feature){
super(cls,feature); // super 在这里是指 父类的 constructor
this.name = name;
this.fea1 = fea1;
this.fea2 = fea2;
}
act(){
console.log(this.name+"爱拉肚子");
super.act();
}
}
// new 一个实例对象
var cat1 = new Buou("叮当","长毛","漂亮","猫","懒");
注意:
如果子类需要继承父类 使用extends关键字
必须在子类的 constructor 中调用super() 在使用this之前调用 此时super()就是父类的constructor方法调用
super关键在写在子类的方法中指向父类本身
静态方法
有些方法 不希望被实例来调用,只能通过类来调用,这种方法就叫做静态方法
class Cat{
// 父类
constructor(name,feature){
this.name = name; //猫类
this.feature = feature;
}
static act(){
console.log(this.name + "打呼噜");
}
act2(){
console.log( this.name + "埋粑粑" );
Cat.act();
}
}
var cat1 = new Cat("小猫咪","懒");
cat1.act2();
// Cat.act();
// cat1.act();
Object.assign() 合并对象 …
Set 是es6中新增的数据结构
创建一个 set
var set = new Set(arr); 会自动去重
数组去重
var arr = [2,3,4,5,6,2,7,6,4,3];
arr = [ ...new Set(arr) ]