JavaScript进阶(十二)

一、bind

    有bind的this是特殊的情况

  •     bind和call,apply功能类似.
  •     call和apply是临时的改变this.
  •     bind是永久改变this.
  •     call是"改变this", bind叫"绑定this"。
  •     bind返回的函数,内部的this,永远指向bind的第一个参数.(不管以何种方式调用)
  •     bind的优先级比call高.

    bind方法:

  •     用来干嘛 => 永久绑定this.
  •     参数问题 => 和call一致.
  •     返回 => 一个和原函数一模一样的函数.(和原函数不是同一个)
  •     原函数.bind(绑定的对象,参数1,参数2,.....).

方式一: 

    function fn() {
      console.log(this)
    }

     fn();// 执行会打印window
     fn.call(document);// 执行会打印document
    function fn() {
      console.log(this)
    }    

    fn = fn.bind(document);

     fn(); // 执行会打印document
     fn.call(window);// 执行会打印document
    //call会引起fn触发.
    fn.call(document);
    //bind不会引起fn的触发.
    fn.bind(document);
    let show = fn.bind(document);
    // show和原函数fn长得一模一样.(和fn不是同一个函数.)
    console.log(show === fn);
    // show里面的this永远指向bind的第一个参数.(注意,受bind影响的是show,而不是fn)
     show(); // document
     show.call(window); // document

     fn(); // window
     fn.call(document.body);// body

方式二:

<script>

    function foo() {
      console.log(this);
    }

    // let show = fn.bind(document);
    // show();

    // 上面的写法可以改成这样.(连写两个())
    foo.bind(document)();

  </script>

二、bind传参 

<script>

    function fn(x) {
      console.log(this);
      console.log(x);
    }

    // bind传参,可以同bind传,也可以通过bind返回的函数传.
    // 两种传参都写,以bind传参为准.
    let show = fn.bind(document, 100);
    show(200);

    // 上面的写法可以改成这样.(连写两个())
    // fn.bind(document)(200);
    // fn.bind(document, 100)(200);

  </script>

三、call和箭头函数 

4种情况的优先级:
        箭头 => bind => call //apply 

let fn = () =>{console.log(this)}
//箭头函数用call来调用,还是以箭头函数为准?
fn.call(document);//window

//以箭头函数为准
fn.bind(document)();//windw

四、bind的应用 

<body>
  <button>按钮</button>
  <p>nnn</p>
</body>
<script>

    let [oBtn, oP] = document.querySelectorAll('body>*');

    // 事件传参
    // 1:
    function show(x){
      console.log(x)
    }

    oBtn.onclick = function() {
      show(100)
    };//100

    function show(x){     
      return function() {
        console.log(x);
      }
    }
    oBtn.onclick = show(100);//100
    // 3:
    function show (x) {
      console.log(x);
      // console.log(this);

    //   // 点击按钮把自己的背景变红.
      this.style.backgroundColor = 'red';
    }
    // 如果第一个参数写null,意即不指定绑定的对象.
    oBtn.onclick = show.bind(null, 100);

    // 点击按钮触发的函数相当于是show,通过bind让show内的this指向oBtn。
    oBtn.onclick = show.bind(oBtn, 100);

    // 通过show来实现oP的点击事件.bind会让this指向oP.
    oP.onclick = show.bind(oP, 100);
    

  </script>

五、私有属性 

属性可以分为私有属性和公有属性:

  • 私有属性.(自定义属性)(手动添加)
  • 公有属性(继承属性)(不手动添加,默认就有的属性)

    方法也分私有和公有:

  • 私有方法(自定义,手动添加的方法)
  • 公有方法(继承方法)(默认就有的方法,默认就能使用的方法)
    // name是obj的属性.(私有属性)
    const obj = {
      name: '小陈',
    }

    // 私有属性age.
    obj.age = 32;

    // toString方法,不是我们手动添加的,它默认就有.是公有方法.
    console.log(obj.toString());
    const arr = [1,2,3];
    
    // 私有方法.
    arr.fn = function() {console.log('这是一个数组')};
    // 调用私有方法.
    arr.fn();
    // 不存在的方法是不能调用的
    // arr.abc();

    // push是默认存在的方法,是公有方法.我们学习过的数组方法都是公有方法.
    arr.push(4);

六、判断是不是私有属性 

 如何判断一个属性是不是私有的?

hasOwnProperty => 判断是不是私有属性的.

  1. 用来干嘛 => 判断一个属性是不是某个对象的私有属性
  2. 参数就是要检测的属性名
  3. 返回布尔值
  4. 对象.hasOwnProperty(属性名);
    const obj = { name: '小陈' };

    // 判断name属性是不是obj的私有属性。
    console.log(obj.hasOwnProperty('name'));
    // 判断toString属性是不是obj的私有属性。
    console.log(obj.hasOwnProperty('toString'));

七、私有和公有的区别 

    私有属性 存储在对象身上.

    公有属性 不存储在对象身上.

    hasOwnProperty => 只能检测属性是不是私有属性。不是私有的一定是公有的吗?

    如果一个属性,既不是私有属性,也不是公有属性,则这个属性对象是无法访问的。

    let obj = {};

    push不是obj的私有属性.

    console.log(obj.hasOwnProperty('push'));// false

    push也不是obj的公有方法。

    obj.push();
    // in => 可以检查一个对象能不能访问某个属性(方法)
    if (!obj.hasOwnProperty('toString') && 'toString' in obj) {
      alert('toString是公有方法')
    }

八、原型 

公有属性存储在哪里 => 存储在原型对象上.

    const obj = {};

    纯对象的原型对象.

    console.log(Object.prototype);

    toString存储在Object.prototype上.(是Object.prototype的私有属性).

    console.log(Object.prototype.hasOwnProperty('toString')); // true
     const arr = [1,2,3];

    // 数组的公有方法,存储在哪里? Object.prototype上吗?

    // 数组的原型对象
    // console.log(Array.prototype);

    // push存储在数组的原型对象上,(是数组原型的私有属性)
    console.log(Array.prototype.hasOwnProperty('push'));

九、原型对象和自定义对象 

 const obj = {};

    obj 继承 Object.prototype (Object.prototype是obj它爹);

    const arr = [];
  •     arr 继承 Array.prototype (Array.prototype是arr它爹);
  •     纯对象继承 Object.prototype
  •     数组对象继承 Array.prototype
  •     Object.prototype构成了一个'家族'.Object是家族名.
  •     Array.prototype构成了另一个'家族'.Array是家族名.
  •     obj是Object家族的一个一份子(实例)
  •     arr是Array家族的一个一份子(实例)

    arr是一个数组实例.

    vm是Vue的一个实例.

十、类 

    原型prototype => 给类中所有的实例添加公有方法,属性 

面向对象 => 类,对象

  •     Array是一个'家族名'
  •     编程世界中,一个家族,一个种群,一个群体,应该叫一个"类".
  •     数组是一个类,纯对象是一个类,函数也是一个类.....
  •     每种数据类型都是一个类.
    // 每种类都有一个原型对象.所有类的实例都继承该类的原型.

    function fn() {
      console.log(1000)
    }

    function show() {
      console.log(2000)
    }
    array => Array
    object => Object
    function => Function
     // 函数原型.
    console.log(Function.prototype);
    
    Function.prototype.car = '保时捷';

    console.log(fn.car);
    console.log(show.car);
    let x = 100;
    let y = 200;

    // number类 => Number
    // number的原型 => Number.prototype

    // Number.prototype.abc = 'number的abc公有属性';

    console.log(x.abc);
    console.log(y.abc);

十一、类和原型 

十二、类都是函数 

  类都是函数

  •     原型 => prototype => 纯对象
  •     原型都是某个函数的私有属性.
  •     每个类都对应一个原型,同时也对应一个函数.
  •     实例的公有方法,存储在类的原型对象上,不是存储在类(函数)上.
  •     实例继承的是类的原型,而不是继承类本身.
  •     prototype就是爸爸.Array就是妈妈,实例就是小孩.
    console.log(Object);
    console.log(typeof Object); // function
    console.log(Array);
    console.log(typeof Array); // function
    console.log(typeof Number);// function
    //prototype是类(函数)的私有属性.
    console.log(Array.hasOwnProperty('prototype'));
  • 给Array的原型对象添加一个abc属性,这样数组实例都可以访问abc.
  • Array.prototype.abc = '公有属性';
  • 给类Array函数添加一个div属性.这个属性,数组实例无法访问.
  • Array.div = '类的属性';
    console.log([].abc);
    console.log([].div);

十三、函数都是类 

  •     类都是函数. => 对
  •     类都有原型prototype属性. => 对
  •     函数都是类 => 不是, 自定义函数是
  •     函数都有prototype属性吗. => 不是, 自定义函数有
  •     系统默认的方法,没有prototype属性.
    console.log(alert.hasOwnProperty('prototype'));
  •  function fn() {};
  •  自定义的方法有prototype属性.有prototype属性意味着fn可以实现一个自定义类.(面向对象)
    console.log(fn.hasOwnProperty('prototype'));

十四、利用原型封装 

方式一: 

    let arr1 = [1,2,3];
    let arr2 = [4,5,6];

    // arr1.forEach(() => {});
    // arr2.forEach(() => {});

    // 给数组的原型添加一个公有方法myForEach.
    // 这个方法内的this,就表示调用这个方法的数组实例.
    Array.prototype.myForEach = function (fn) {
      for (let i = 0; i < this.length; i++) {
        fn(this[i], i);
      }
    };

    arr1.myForEach((item, i) => {console.log(item)});
    arr2.myForEach((item, i) => {console.log(item)});
    // 封装一个window的方法。处理不同数组需要传递不同的数组作为参数
    function myForEach(arr, fn) {
      for (let i = 0; i < arr.length; i++) {
        fn(arr[i], i);
      }
    };

    myForEach(arr1,(item, i) => {
      console.log(item);
    });

    myForEach(arr2,(item, i) => {
      console.log(item);
    });

方式二:

    String.prototype.fn = function (i) {
      
      // if (i < 0) {
      //   i = i + this.length;
      // }
      // return this.slice(i, i+1);

      return this.substr(i, 1);
    }

    let str = 'abcd';

    let msg = str.fn(1);

    console.log(msg); // d

十五、原型链 

  •     obj继承Object.prototype => obj的爸爸是Object.prototype
  •     arr继承Array.prototype. => arr的爸爸是Array.prototype
  •     Array.prototype的爸爸又是谁 => Object.prototype

  •     原型链 => 用于面试对象间的一种继承关系.
  •     所有原型链的顶层对象都是 Object.prototype (null)
  •     任何类的实例,都可以访问Object.prototype的方法.
  •     实例可以访问它原型链上的所有方法.

  •     arr => Array.prototype => Object.prototype (原型链)
  •     100 => Number.prototype => Object.prototype
  •     alert => Function.prototype => Object.prototype
<script>

    let arr = [1,2,3];
    let obj = {};

    Object.prototype.abc = 'uuuuuu';
    console.log(Array.prototype.abc);

    console.log([].abc);
    console.log('字符串'.abc);
    console.log((100).abc);
    console.log(true.abc);
    console.log(alert.abc);

    // 如果原型链上有相同的属性,则实例默认访问的是"最近"的那个属性.
    Array.prototype.abc = 'wwwwww';
    console.log([].abc);

  </script>

十六、原型链和作用域链 

  • 作用域链 => 非面向对象 => 确定某个作用域内能访问到的变量,作用域链确定了,能访问到的变量也就确定了.
  •     原型链 => 面向对象 => 确定某个实例能访问到的属性(方法),原型链确定了,能访问到的属性(方法)就确定.

    作用域链 => 实现变量查找

  •     1:先写除作用域链.
  •     2:沿着作用域链查找,查找变量声明,找到就停止查找.(就近原则).找到全局作用域,如果还没有就报错.

    原型链 => 实现属性查找

  •     1:先写原型链.
  •     2:沿着原型链查找,查找当前的原型对象有没有对应的私有属性,有就停止查找(就近原则),找到Object.protorype,

       如果还没有,得到undefined.

 arr => Array.prototype => Object.prototype

     1: 先看看arr有没有abc私有属性.如果有就是它,停止往上查找
     2: 如果arr没有abc私有属性,看看Array.prototype有没有abc私有属性.如果有就是它,停止往上查找
     3: 如果Array.prototype没有abc私有属性,看看Object.prototype有没有abc私有属性.如果有就是它.没有就返回undefined

    let arr = [1,2,3];

    console.log(arr.abc);
    // in => 检查对应的属性是不是存在于实例的原型链上.
    let obj = {name: '幂幂'};
    Object.prototype.age = 32;
    // for in 可以检测变量实例原型链上的可枚举的属性.
    // 可枚举 => 可for in.
    for (let key in obj) {
      console.log(key);
    }

十七、__proto__ 

<body>

  <div id='wrap'></div>
</body>

 

<script>

    // 如何获取一个对象的上一级原型对象.

    let arr = [1,2,3];  
    let oDiv = document.getElementById('wrap');

    // Array.prototype 就是 arr的上一级原型对象.
    // Object.prototype 就是  Array.prototype 的上一级原型对象.
    // arr => Array.prototype => Object.prototype

    // 获取arr的上一级原型对象.(双下划线)
    // console.log(arr.__proto__);

    // 获取oDiv直接继承的原型(上一级原型对象)
    console.log(oDiv.__proto__);

    // 如果我想给所有的div标签都添加一个公有方法.

  </script>

 oDiv 
     => HTMLDivElement.prototype
     => HTMLElement.prototype
     => Element.prototype
     => Node.prototype
     => EventTarget.prototype
     => Object.prototype

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值