24_原型和原型链_this

发布于:2025-03-30 ⋅ 阅读:(47) ⋅ 点赞:(0)

目录

一、this关键字

修改this的指向

二、原型和原型链

三、创建对象

通过构造函数创建 (es5)

通过类创建 (es6)

四、浅拷贝和深拷贝 ctrl+c

浅拷贝: 只拷贝一层

深拷贝: 可以拷贝多层


 

一、this关键字

每个函数内部都有一个this,函数内部的this和函数的调用方式有关,和定义方式无关!

  //1- 全局的this 指向的window  window是顶级对象
  //2- 每个普通函数中  都有一个this  (函数内部的this和函数的调用方式有关,===>谁调用this就执行谁)
  //3- 在事件处理函数中,this指向触发事件的元素
  //4- 全局函数(window)  立即执行函数  匿名函数(window)  this 都执行window
  //5- 箭头函数的this  箭头函数没有自己的this  箭头头函数的this指向箭头函数的上下文的this
  //6-构造函数 加new 调用  构造函数内部的this 指向 构造函数创建的对象
       // - 构造函数内部方法 中的this  执行的是调用方法的对象

修改this的指向

1- 借助that 保存

  <!-- 
        改变this的方法
        1- 函数外部定义that  that的值是想要的this  在函数内部使用that
          就可以在函数内使用到外部的this;
     -->
    <script>
        function Dog(name){
            this.username = name;
            console.log(this);//d1
            let that = this;//that ==>d1
            this.run =function(){
                console.log(this);//d1
                setTimeout(function(){
                    console.log(this);//window
                    console.log(that.username);//undifined
                },1000)
            }
        }
        let d1 = new Dog("小黑");
        d1.run();
    </script>

2- 使用箭头函数

   function Dog(name){
            this.username = name;
            console.log(this);//d1
            this.run =function(){
                console.log(this);//d1
                setTimeout(()=>{
                    console.log(this);//window
                    console.log(this.username);
                },1000)
            }
        }
        let d1 = new Dog("小黑");
        d1.run();

3- call 、 apply bind 修改this的指向

  function fn(a,b){
            console.log(this);//
            console.log(a,b);
  }
        // fn(1,2);
        // 借助call方法 调用函数  里边写参数  第一个参数可以修改this的指向,,后边的参数都是你调用函数传的实参
        let obj = {name:"李白"}
        // fn.call(obj,1,2);

        // 借助apply 方法 调用函数  里边写参数,第一个参数可以修改this的指向,第二个参数是个数组,数组的内容你调用函数传的实参
        // fn.apply(obj,[1,2]);

        // 借助bind方法 
        // 不会直接调用fn函数,返回一个新的函数,新的函数和fn函数是一样的,
        // 区别就是在新函数中执行的时候this 是第一个参数传入的对象,如果有实参 在调用新函数时传入
        let newFn = fn.bind(obj);
        newFn(1,2)

二、原型和原型链

原型 解决了构造函数的一个缺点

// 什么原型?
        // 每一个构造函数中都有一个prototype(显式原型)属性,这个就叫原型,上面存了构造函数的方法,
        // 每个对象都有一个__proto__(隐式原型) 属性   指向了构造函数的prototype

        // 当我们使用对象的属性或者方法时,先在自身查找,找不到就去__proto__找
  // 原型链?
            // 当我们使用对象的属性或者方法时,先在自身查找,找不到就去__proto__找
            // 如果__proto__没有 继续找  因为__proto__也是个对象 所以它也有__proto__属性
            // d1.__proto__.__proto__  以此类推
            // 直到找到Object.prototype 就结束了,因为Object.prototype的原型是null
<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>

<body>
    <script>
        // 构造函数创建对象  相当于是个模板  
        function Dog(name) {
            this.name = name;
        }

        Dog.prototype.run=function(){
            console.log(this.name + "会跑");
        }
        let d1 = new Dog("小黑");
        let d2 = new Dog("小白");
        // 地址不一样  堆和栈
        console.log(d1 === d2);//false
        // 栈内存
        // d1:001
        // d2:002

        // 堆内存
        // 001 {name:"小黑",run:003}
        // 002 {name:"小白",run:004}

        // 003  function(){}
        // 004  function(){}
        // .....
        console.log(d1.run === d2.run);//


        // js内置的构造函数  怎么样做的??
        // 公共的方法  都放到了原型(prototype)里
        // 原型里方法  只占用一块内存空间,每次用这个方法时用的都是同一个

        let arr1 = new Array(1, 2, 3);
        let arr2 = new Array(4, 5, 6);
        console.log(arr1 === arr2);//false
        console.log(arr1.push === arr2.push);//true arr1 和 arr2 的push方法 存到一个地址里边

        // 什么原型?
        // 每一个构造函数中都有一个prototype(显式原型)属性,这个就叫原型,上面存了构造函数的方法,
        // 每个对象都有一个__proto__(隐式原型) 属性   指向了构造函数的prototype

        // 当我们使用对象的属性或者方法时,先在自身查找,找不到就去__proto__找

        console.log(Array.prototype.push === arr1.__proto__.push);//true
        console.log(Array.prototype.push === arr2.__proto__.push);//true
        console.log(Array.prototype === arr1.__proto__);//true
        console.log(Array.prototype === arr2.__proto__);//true
        console.log(arr1.push);


        // 原型链?
            // 当我们使用对象的属性或者方法时,先在自身查找,找不到就去__proto__找
            // 如果__proto__没有 继续找  因为__proto__也是个对象 所以它也有__proto__属性
            // d1.__proto__.__proto__  以此类推
            // 直到找到Object.prototype 就结束了,因为Object.prototype的原型是null

        // hasOwnProperty  判断对象中有没有某个属性
        console.log(d1.__proto__.__proto__);
        console.log(d1.__proto__.__proto__.__proto__);//null
        console.log(d1.__proto__.__proto__==Object.prototype);//true

    </script>
</body>

</html>

三、创建对象

通过构造函数创建 (es5)

通过类创建 (es6)

通过类创建 是 通过构造函数创建的一个语法糖


    //    通过类创建对象   语法糖
    // 其实就是对通过构造函数创建的 一种封装

   // 定义类  class

//  实例属性和方法   必须创建对象  在创建的对象上使用  new
//  静态属性和方法   可以不创建 直接Person.XXX 使用
    class Person{
        // 类在使用时  必须加new  一旦new了  就会自动的执行constructor函数
        // constructor 里一般放属性
        constructor(name){
            // let this = {}
            this.name = name; //实例属性/对象属性
        }

        // 直接定义Person类的方法   直接定义到原型上了
        study(){  //实例方法/对象方法
            console.log(this.name+"会学习");
        }

        // 静态属性  static
        static a = 1;
        static test(){
            console.log("test");
        }
    }

//    let p1 = new Person("小张");
//    let p2 = new Person("小李");
//    console.log(p1.study === p2.study);//true  
console.log(Person.a);
console.log(Person.test);

四、浅拷贝和深拷贝 ctrl+c

浅拷贝: 只拷贝一层

浅拷贝 拷贝的是地址 只要有一个改了 其他的都会改

列举了三种浅拷贝的写法

  // 1- 展开运算符...  实现浅拷贝
        let obj2 = {
            ...obj1
        }

        obj2.name = "123"
        // console.log(obj1,obj2);

        // 2- Object.assign()  实现浅拷贝
        let obj3 = {}
        Object.assign(obj3, obj1);
        //obj3.child.name = "123";
        //console.log(obj1,obj3);

        // 3- 循环遍历实现浅拷贝
        function copy(obj) {
            let obj4 = {}
            // 循环对象
            for (let key in obj) {
                // console.log(key,obj1[key]);
                obj4[key] = obj[key]
            }
            return obj4;
        }

       let res = copy(obj1)
       console.log(res);

 

深拷贝: 可以拷贝多层

深拷贝就是完全拷贝一个对象的所有属性值,

如果第二层以后的属性值是一个对象, 不要直接把地址赋值,而是创建一个新的对象!!

  // 深拷贝就是完全拷贝一个对象的所有属性值,
        // 如果第二层以后的属性值是一个对象, 不要直接把地址赋值,而是创建一个新的对象!!
         var obj1 = {
            name:"张三",
            child:{
                name:"张三丰",
                child:{
                    name:"小张三丰",
                }
            }
        }

        // 递归  实现深拷贝   (如果对象里由很多属性,写循环跟简单)
        // 递归:  函数自己调用自己

        function copyDeep(obj){
            let obj2 = {}
            for(let key in obj){
               let value = obj[key];
            //  如果value 不是对象 那么value给到obj2
            //  如果value  是对象  那么继续定义一个新的对象 接着遍历第二层对象
                if(typeof value == "object"){
                    // 继续调用函数本身
                   let newObj = copyDeep(value);
                   obj2[key] = newObj
                }else{
                    obj2[key] = value
                }
            }
           return obj2;
        }

       let res = copyDeep(obj1);
       console.log(res);
       console.log(res === obj1);//false

浅拷贝是地址的传递

深拷贝是值的传递

实现深拷贝的方案

1- 借助递归实现

2- 把对象转为json字符串,在利用parse 把字符串解析一个新的属性

 		 let res = JSON.stringify(obj1);
         let newObj = JSON.parse(res);
         console.log(obj1===newObj);
         console.log(obj1.child===newObj.child);
         console.log(obj1.child.child===newObj.child.child);

3- 借助第三方库实现 lodash

想要用别人的库 要下载 应用

推荐网站: BootCDN - Bootstrap 中文网开源项目免费 CDN 加速服务 铂特优选 可以从里边下载 你想要的库

lodash官网:Lodash 简介 | Lodash中文文档 | Lodash中文网

let newObj = _.cloneDeep(obj1);

 


网站公告

今日签到

点亮在社区的每一天
去签到