JavaScript 高级语法 第四章

JavaScript 高级

面向对象

this:

了解函数中 this 在不同场景下的默认值,知道动态指定函数 this 值的方法

问:this到底指向谁?

答:谁调用这个函数,this就指向谁 通常指向调用者

<script>
	// 普通函数
    function fn(){
        console.log(this);  // 指向Window 调用者
    }
    
    // 事件处理函数
    document.addEventListener('click',function(){
        console.log(this);  // 指向事件源 调用者
    })
    
    // 构造函数
    function Person(){
        console.log(this);  // 指向new的实例对象
    }
    new Person();
    
    // 方法
    let obj = {
        uname:'张飞',
        fei:function(){
            console.log(this);  // 指向调用者 obj
        }
    }
    obj.fei();
    
    // 定时器
    window.setInterval(function(){
        console.log(this);  // 指向Window,因为Window是调用者
    },1000)
    
    // 自执行函数
    (function(){
        console.log(this);  // 指向Window 调用者
    })()
</script>

严格模式:

严格模式:‘use strict’

开启严格模式:“use strict”(作用域也不一样,取决于放哪里)

注意:必须放到作用域最开头的位置

<script>
	// 1.变量必须声明
    console.log(n);  // n is not defined
    
    // 2.普通函数的this,不再指向Window,而是undefined
    function fn(){
        console.log(this);  // undefined
    }
    fn();
    
    // 3.函数形参不可以重名
    function fn(a,a){
        console.log(a + a); // Duplicate parameter name not allowed in this context
    }
    fn(1,2);
------------------------------------------------------------------------------------------------------------
    **上述例子,如果关闭严格模式,结果为:
    function fn(a,a){
        // a = 1;(被覆盖)
        // a = 2;
        console.log(a + a);  // 4
    }
    fn(1,2); // 好比与 重新赋值
</script>

箭头函数:

  1. 箭头函数中的 this 与普通函数完全不同,也不受调用方式的影响,事实上箭头函数中并不存在 this
  2. 箭头函数中访问的 this 不过是箭头函数所在作用域的 this 变量
  3. 事件回调函数使用箭头函数时,this 为全局的 window
  4. 基于原型的面向对象也不推荐采用箭头函数

call方法:

call:函数.call(this的指向,参数1,参数2,参数n…);
<script>
    // 定义一个函数
	function fn(a,b){
        console.log(this,a,b);  // {uname: '阿飞', age: 22} 1 2   此时的this已经指向了obj
        console.log(a + b);     // 3
    }
    fn();
    
    // 定义一个对象
    let obj = {
        uname:'张飞',
        age:22
    };
    
    // 函数.call(this的指向,参数1,参数2)
    fn.call(obj,1,2);
</script>
call练习:
<script>
    // 定义一个对象
	let obj = {
        uname:'李',
        eat:function(){
            console.log(this);  // {uname:'四'}
        }
    }
    
    // 再次定义一个对象
    let o = {
        uname:'四'
    }
    
    // 使用call修改obj中this的指向
    obj.call(o);  // this指向了o这个对象,上面的console.log(this) 打印的就是o对象里面的内容
</script>

apply方法:

apply:函数.apply(this的指向,[参数1,参数2,参数n…]) 传的值必须放在数组里面进行传递
<script>
    // 定义一个函数
	function fn(a,b){
        console.log(this,a,b);  // {uname: '哇哈哈', age: 22} 12 22
    }
    
    // 定义一个对象
    let o = {
        uname:'哇哈哈',
        age:22
    }
    
    // 函数.apply(this的指向,[参数1,参数2]);
    fn.apply(o,[11,22]);  // 传的是数组,但是传到函数里面的时候就会自动拆分出来
</script>
apply:绝大多数用于数组
<script>
	// 数组求最大值
------------------------------------------------------------------------------------------------------------
    // 第一种方法:正常forEach
    let arr = [22,255,33,147];
    let max = arr[0];
    arr.forEach(function(item){
        if(item > max) max = item;
    })
    console.log(max);  // 255
------------------------------------------------------------------------------------------------------------
	// 第二种方法:Math.max.apply
	let arr = [22,255,33,147];
    // Math.max 不加括号,即为一个不调用的函数
    let re = Math.max.apply(null,arr); // null表示不修改this指向,后面参数表示把数组传进去,arr本身就是一个数组
    console.log(re);   // 255
------------------------------------------------------------------------------------------------------------
	// 第三种方法:使用剩余符号(...)
	// ... : 剩余符 或者 展开运算符
	// 作用:① 接收参数的话就是接收剩余值  ② 对于复杂类型的话就是展开运算符
	let arr = [22,255,33,147];
    console.log(...arr);  // 展开数组,将数组展开 (22 255 33 147)
    let re = Math.max(...arr);  // 将展开的数组,通过Math.max这个方法直接获取最大值
    console.log(re);   // 255
</script>
apply练习:
<script>
	let obj = {
        uname:'aaa',
        fei:function(){
            console.log(this);  
        }
    }
    let o = {
        uname:'abc'
    }
    
    // 使用apply方法修改obj的this指向
    // 函数.apply(this的指向,[参数1,参数2]);
    obj.fei.apply(o);  // obj里面的fei方法,所以要加上obj.
</script>

bind方法:

bind:函数.bind(this的指向,参数1,参数2,参数n…)
<script>
    // 定义一个函数
	function fn(a,b,c){
        console.log(this,a,b,c);
    }
    
    // 定义一个对象
    let o = {
        uname:'aaa'
    }
    
    // 通过bind方法更改this的指向
    // 注意:这里fn调用bind,但是bind不会回头调用fn,只是悄悄把this更改了
    // bind一般用于改变this,但是不希望自动执行
    let re = fn.bind(o,1,2,3); // 或者 fn.bind(o)()
    re();  // 用re变量接收一下,再调用一下re;
</script>
bind练习:
<body>
    <input type='button' value='点击获取验证码'>
</body>
<script>
    // 获取元素
	let btn = document.querySelector('input');
    // 绑定单击事件
    btn.addEventListener('click',function(){
        // 立即禁用元素
        this.disabled = true;
        // 我希望5秒后解除
        setTimeout(function(){
            // 5秒后解除
            // 此时的this指向仍然是window,this指向错误,所以解除禁用不会执行
            // 用bind更改this指向  在函数后面直接点(.)
            // bind里面直接写this(this是写在setTimeout外面,this不指向window,指向的是当前事件源 btn)
            // 我们希望不立即解除,而是等待5秒执行,所以不能用call
        }.bind(this),5000)
    })
</script>

总结:

语法:
  1. call:函数.call(this的指向,参数1,参数2,参数n…)
  2. apply:函数.apply(this的指向,[参数1,参数2,参数n…])
  3. bind:函数.bind(this的指向,参数1,参数2,参数n…)
相同点:
  • 都可以改变this指向
不同点:
  1. apply:第二个参数只能是数组
  2. bind:不回调函数,而是返回一个相当于改了this的对象

类和对象

类:泛泛概念
对象:具体概念
类的基本使用:
<script>
	// 创建类的语法
    class 类名 {} (首字母大写)
----------------------------------
    // 创建实例对象
    new 类名()
</script>
<script>
    // 例子
	class Person {}
    let p1 = new Person();
    // instanceof用于判断是否属于构造函数
    console.log(p1 instanceof Object);  // true
-------------------------------------------------
    class Dog {}
	let d1 = new Dog();
    console.log(d1 instanceif Object);  // true
</script>

类的操作(增加成员)

成员分为两类:静态成员实例成员

实例成员:属性 = 值

  1. 关键字class封装了所有的实例属性和方法
  2. 类中封装的并不是变量和函数,因此不能使用关键字 let const var

静态成员:static 属性 = 值

  1. static 关键字用于 声明静态属性和方法
  2. 静态属性方法 直接通过 类名 进行访问
添加实例成员:
<script>
	// 添加实例成员
    class Person {
        // 实例属性
        // 一定不加let
        uname = '张三丰';
        age = 22;
    	head = 1;
    
    	// 实例方法
    	// 不用加function
    	eat() {
            console.log('eat');
        }
    	say() {
            console.log('say');
        }
    }
    
    // 实例化
    let p1 = new Person();
    console.log(p1);   // Person {uname: '张三丰', age: 22, head: 1}
    // 方法放进了原型对象prototype里面
    p1.eat();  // eat
</script>
添加静态成员:
<script>
	// 添加静态成员(供类使用)
    class Person {
        // 静态属性,在属性名前加static
        static language = '汉语';
    	static skin = '黄皮肤';
    
    	// 静态方法
    	static sleep(){
            console.log('sleep')
        }
    	static walk(){
            console.log('walk')
        }
    }
    
    // 实例化对象
    let p1 = new Person();
    // 实例成员上没有成员  直接加到了原型对象prototype的constructor 的class里面了
    console.log(p1);  // Person {}
    // Person仍可使用
    console.log(Person.language);  // 汉语
</script>
添加静态成员练习:
<script>
	// 静态成员
    class Person {
		// 添加静态成员
        static language = '汉语';
    
    // 静态方法
    static eat() {
		console.log('eat')
    }
    static run() {
        console.log('run')
    }
}

	// 实例化
	let p1 = new Person();
	// 方法存在了constructor class里面
	console.log(p1);
	>> 控制台输出:
		`Person {}
		[[Prototype]]: Object
			constructor: class Person
			[[Prototype]]: Object
		`
    Person.eat();  // eat
    Person.run();  // run
</script>

构造函数

创建类时在类的内部有一个特定的方法 constructor ,该方法会在类被实例化时自动被调用,常被用于处理一些初始化的操作
构造函数
  1. 在es6中,constructor被称为构造函数,构造方法,构造器
  2. 类里面有一个固定的方法名constructor(构造器) 用来接收参数作初始化操作
  3. 构造函数在new的时候 自动执行不需要调用
  4. 构造函数如果不写的话,那么内部会 默认的添加上这个方法
  5. 类里面 一定有构造器,就看用不用
  6. 构造器的名字 不能修改 ,只能叫 constructor
  7. 构造器 只能有一个
<script>
	class Person {
        // 此处仍然可以写
        aaa = 'aaa';
        
        // 类里面有一个固定方法名,constructor,用来接收参数作初始化操作
        constructor(uname,age,gender){
            // 构造方法
            this.uname = uname;
            this.age = age;
            this.gender = gender;
            
            // 这里同样也能添加共同属性
            this.head = 1;
        }
        say(){
            console.log('say')
        }
        eat(){
            console.log('eat')
        }
    }
    
    // 实例化操作
    let p1 = new Person('张飞','22','男');
    console.log(p1);  // Person {aaa: 'aaa', uname: '张灰', age: 22, gender: '男', head: 1}
    let p2 = new Person('关羽','23','男');
    console.log(p2);  // Person {aaa: 'aaa', uname: '关羽', age: 23, gender: '男', head: 1}
    let p3 = new Person('貂蝉','21','女');
    console.log(p3);  // Person {aaa: 'aaa', uname: '貂蝉', age: 21, gender: '女', head: 1}
</script>

继承

extends:
  1. extends 是专门用于实现继承的语法关键字
  2. 如果想要实现继承,用extends定义子类去继承父类
super:
  1. 在继承的过程中子类中 constructor 中必须调 super 函数,否则会有语法错误
  2. 子类构造函数中的 super 函数 的作用是可以 将子类实例化时获得的参数传入父类的构造函数之中
  3. Super用于调用父类的方法

继承:
<script>
	// 定义一个类
    class Father {
        constructor(uname,age){
            this.uname = uname;
            this.age = age;
        }
        qian(){
            console.log('赚一个亿')
        }
    }
    
    // 继承:
    // extends:声明一个类为子类
    class Son extends Father {
        constructor(uname,age,score){
            // 在继承时,如果子类有自己的构造器,那么必须使用super调用父类的方法(Father(的属性值))
            // super:调用父类的方法
            // 必须先使用super调用父类的构造器,然后再让自己添加成员
            super(uname,age);
            this.score = score;
        }
    }
    
    // 实例化测试是否继承完成
    let s1 = new Son('儿子',20,99)
    console.log(s1);  // Son {uname: '儿子', age: 20, score: 99}
    // 调用
    s1.qian();  // 赚一个亿
</script>
继承练习:
<script>
	// 定义一个类
    class People {
        constructor(uname,age,sex){
            this.uname = uname;
            this.age = age;
            this.sex = sex;
            this.head = 1;
        }
        
        // 定义方法
        say() {
            console.log('say')
        }
        eat() {
            console.log('eat')
        }
    }
    
    // 再次定义一个继承People类
    // 语法:class 新类名 extends 父类名
    class Chinese extends People {
        constructor(uname,age,sex,shuai) {
            // 在设置之前调用super,调用父People的形参,有什么写什么
            super(uname,age,sex)
            this.shuai = shuai;
        }
    }
    
    // 实例化对象
    let c1 = new Chinese('中国人',1,'男','帅');
    console.log(c1);  // Chinese {uname: '中国人', age: 1, sex: '男', head: 1, shuai: '帅'}
</script>

类 的 本 质 就 是 函 数 = > f u n c t i o n 类的本质就是函数 => function =>function

let obj = new Person();
console.log(typeof obj);  // function

拷贝

  1. 拷贝不是直接赋值
  2. 直接赋值是传地址,共用一份数据,一方更改值,另一方也会跟着更改
浅拷贝:只拷贝最外面层
<script>
	// 浅拷贝:只拷贝最外层
    let obj = {
        uname:'张三丰',
        age:22,
        gender:'男'
    }
    // 空对象
    let newObj = {};
    
    // 循环遍历
    // 添加值,原本有值就是修改值,原本没有就是添加值
    
    // for(let key in 需要被拷贝的对象){
    //	新对象[key] = 被拷贝的对象[key]
    // }
    
    // 将有内容的对象拷贝到空对象里面
    for(let k in obj){
        newObj[k] = obj[k]
    }
    
    obj.uname = '哇哈哈'// 两个对象之间互不影响
    console.log(obj,newObj)
    // {uname: '哇哈哈', age: 22, gender: '男'}    {uname: '张三丰', age: 22, gender: '男'}
</script>
浅拷贝方法:Object.assign(newObj(空对象, 有内容的对象))

O b j e c t . a s s i g n ( n e w O b j ( 空 对 象 , 有 内 容 的 对 象 ) ) Object.assign(newObj(空对象,有内容的对象)) Object.assign(newObj())

深拷贝:全部层拷贝
<script>
	// 深拷贝:所有层都拷贝
    let obj = {
        uname:'张三丰',
        age:22,
        gender:'男',
        color:['red','blue','yellow'],
        message:{
            index:6,
            score:99
        }
    }
    // 空对象
    let newObj = {};
    
    // 用来拷贝的函数
    function kaobei(newObj,obj){
        for(let key in obj){
            //obj[key]可能是数组
            //obj[key]可能是对象
            //不能再直接赋值
            
            //如果是数组,它的构造函数一定是Array
            //如果为true,就能说明obj[k]是数组
            if(obj[key] instanceof Array) { // 可能是数组
                 // obj[k] = ['red','blue','yellow'];
                 // 保证新对象里面也是一个数组
                 newObj[key] = [];
                
                 // 拷贝(递归,调用自己)
                 // 拷贝(参数)
                 kaobei(newObj[key],obj[key]);
                
                 // 如果是对象,它的构造函数一定是Object
                 // 如果为true,只能说明obj[key]是对象
            } else if(obj[key] instanceof Object){ // 可能是对象
                 // newObj[key] = {index:6,score:99}
                 // 保证新对象里面也是一个对象
                 newObj[key] = {};
                 
                 // 拷贝(递归,调用自己)
                 // 拷贝(参数)
                 kaobei(newObj[key],obj[key]);
            }else{
                 newObj[key] = obj[key];   
            }
    	}
	}
    // kaobei(前面是一个空对象,后面是一个有内容的对象)
    kaobei(newObj,obj);
    newObj.message.index = 666;
    console.log(obj,newObj);
    >> 控制台输出: `index值被修改了,而且不影响另一个对象`
    
    `
	obj:
	{uname: '张三丰', age: 22, gender: '男', color: Array(3), message: {…}}
		age: 22
		color: (3) ['red', 'blue', 'yellow']
		gender: "男"
		message: {index: 6, score: 99}
		uname: "张三丰"
		[[Prototype]]: Object
------------------------------------------------------------------------------
	newObj:
	{uname: '张三丰', age: 22, gender: '男', color: Array(3), message: {…}}
		age: 22
		color: (3) ['red', 'blue', 'yellow']
		gender: "男"
		message: {index: 666, score: 99}
    	uname: "张三丰"
		[[Prototype]]: Object
	`
</script>
  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值