JavaScript高级

一、this的指向

运行的时候才决定this的指向

      function foo() {
      console.log("foo函数",this)
      
      }


    //   1.直接调用
    foo()// this => window

    // 2.调用方式二,将函数放在一个对象当作
    // 对象的一个属性
    var obj = {
      foo: foo,
    }
    obj.foo() // this => obj

this的绑定规则

1.默认绑定 =>就是独立函数调用

      function foo() {
      console.log("foo函数",this)
      
      }

    //   1.直接调用
    foo()// this => window

// 2.函数定义在对象当中,但是是独立调用
var obj = {
    name: "obj",
    foo: function() {
      console.log("foo函数", this);
    },
}
var bar = obj.foo;

bar() // this => window

//3.高阶函数
      function foo(fn) {
        fn();
      }
      var obj = {
        name: "cui",
        bar: function () {
          console.log("bar函数执行", this);//window
        },
      };
      foo(obj.bar)

        严格模式下,独立函数调用的this指向的是undefined

    <script>
      "use strict";

      // 3.严格模式下 ,独立函数的调用this指向的时undefined
      function fn() {
        console.log(this);//undefined
      }
      fn();
    </script>

2.隐式绑定

1.
      function foo() {
        console.log("foo函数", this); //obj
      }
      var obj = {
        bar: foo,
      };
      obj.bar();

2.
function foo(){
    console.log("foo函数",this)//obj1
}

var obj1 = {
    foo:foo,
    name:'obj1'
}
var obj2 = {
    obj1:obj1
}
obj2.obj1.foo()

3.new绑定

      /*
1.创建一个新的空对象
2.将this指向这个新对象
3.执行函数体中的代码
4.没有显示返回非空对象时,返回这个对象

*/
      function foo() {
        console.log("foo函数", this);//foo
        this.name = "cui";
      }
      new foo();

4.显示绑定

4.1 apply(this,[传入的参数])     第一个参数this,第二个参数传入一个数组  并调用函数

      function foo(name,age,hegiht) {
        console.log("this is", this, name,age,hegiht); // this is cui ["chen", 18, 180]
      }
      foo.apply("cui", ["chen", 18, 180]);

4.2 call(this,参数一,参数二)

      function foo() {
        console.log("this",this)
      }
      foo.call("cui","chen",18,180)

4.3 bind 创建一个新的函数改变this指向,不会自动执行函数

      function foo(name, age, hegiht, adds) {
        console.log("foo this is", this); //obj
        console.log(name, age, hegiht, adds); //xiong 18 180 cd
      }
      const obj = { name: "cui" };

      const bar = foo.bind(obj, "xiong", 18, 180);
      bar("cd");

内置函数的调用绑定

1.定时器(this =>window) 按钮点击事件 (this => button)

2.forEach(函数,this) 箭头函数this指向window

const arr = ["cui", "chen", "zhi"];

arr.forEach(
    function () {
        console.log("this is", this);
    }, "aaa");

比较优先级

1.显示绑定的优先级高于隐式绑定

2.new绑定优先级高于隐式绑定,new绑定不能与call和apply同时使用。new绑定的优先级高于bind

3.bind优先级高于apply/call

this绑定之外的情况

情况一:

1.非严格模式下 this绑定 null undefined 指向window 

2.严格模式下 对应 null undefined

    <script>
      "use strict";
      function bar() {
        console.log(this)
      }
      bar.apply("aa")
      bar.apply(null)
      bar.apply(undefined)

    </script>

情况二:    (obj2.foo = obj1.foo)()  在这个时候表示独立函数调用,指向window

const obj1 = {
        name: "obj1",
        foo: function () {
          console.log("foo", this);
        },
      };
      const obj2 = {
        name: "obj2",
      };
      (obj2.foo = obj1.foo)() //在这个时候表示独立函数调用,指向window

二、箭头函数

1.箭头函数不会绑定this、arguments属性

2.不能作为构造函数使用(不能和new一起来使用,会抛出错误)

完整写法

    

      const foo  =()=>{
        console.log("箭头函数的函数体")
        } 
    

简写箭头函数

1.如果函数体当中只有一行代码,可以省略{}不能写return

      const bar = ()=>console.log("箭头函数的函数体")

2.函数的参数只有一个时,可以省略()

    const arr = ["cui","chee","chen","zhao"]
    arr.map(item=>console.log(item))

3.如果函数返回是一个对象,必须加小括号()

      const foo = () => ({ name: "崔" });

      console.log(foo());//{ name: "崔" }

 箭头函数的this

        箭头函数压根没有this,查找规则会使this去找上层作用域

      const bar = ()=>console.log("箭头函数的函数体",this)
      bar()//window

this的查找规则 

这里箭头函数没有this,但是会去上层作用与查找也就是foo这个函数,这个函数又被obj调用所以这里的this指向obj(注意对象的{}不是作用域)

      const obj = {
        name: "obj",
        foo: function () {
          const bar = () => {
            console.log("bar", this); //obj
          };
          return bar;
        },
      };
      const fn = obj.foo();
      fn();

面试题

var name = "window";
var person = {
  name: "person",
  sayName: function () {
    console.log(this.name);
  }
};
function sayName() {
  var sss = person.sayName;
  sss(); // window
  person.sayName(); // person
  (person.sayName)(); // person
  (b = person.sayName)(); // window 
}
sayName();
var name = 'window'
var person1 = {
  name: 'person1',
  foo1: function () {
    console.log(this.name)
  },
  foo2: () => console.log(this.name),
  foo3: function () {
    return function () {
      console.log(this.name)
    }
  },
  foo4: function () {
    return () => {
      console.log(this.name)
    }
  }
}

var person2 = { name: 'person2' }

// person1.foo1(); // person1
// person1.foo1.call(person2); // person2

// person1.foo2(); // window
// person1.foo2.call(person2); // window

// person1.foo3()(); // window
// person1.foo3.call(person2)(); // window
// person1.foo3().call(person2); // person2

// person1.foo4()(); // person1
// person1.foo4.call(person2)(); // person2
// person1.foo4().call(person2); // person1
var name = 'window'
function Person (name) {
  this.name = name
  this.foo1 = function () {
    console.log(this.name)
  },
  this.foo2 = () => console.log(this.name),
  this.foo3 = function () {
    return function () {
      console.log(this.name)
    }
  },
  this.foo4 = function () {
    return () => {
      console.log(this.name)
    }
  }
}
var person1 = new Person('person1')
var person2 = new Person('person2')

// person1.foo1() // person1
// person1.foo1.call(person2) // person2

// person1.foo2() // person1
// person1.foo2.call(person2) // person1

// person1.foo3()() // window
// person1.foo3.call(person2)() // window
// person1.foo3().call(person2) // person2

// person1.foo4()() // person1
// person1.foo4.call(person2)() // person2
// person1.foo4().call(person2) // person1
var name = 'window'
function Person (name) {
  this.name = name
  this.obj = {
    name: 'obj',
    foo1: function () {
      return function () {
        console.log(this.name)
      }
    },
    foo2: function () {
      return () => {
        console.log(this.name)
      }
    }
  }
}
var person1 = new Person('person1')
var person2 = new Person('person2')

// person1.obj.foo1()() // window
// person1.obj.foo1.call(person2)() // window
// person1.obj.foo1().call(person2) // person2

// person1.obj.foo2()() // obj
// person1.obj.foo2.call(person2)() // person2
// person1.obj.foo2().call(person2) // obj

三、函数跟对象

函数的属性

      //定义函数
      function foo(a, b, c = 5, ...other) {}
      function bar(m, n) {}
      foo.message = "Hello, world!";
      console.log(foo.message); //Hello, world!
      //默认函数是有自己的属性的 ,函数默认有name属性就是函数的名字
      //1.name属性
      console.log(foo.name); //foo

      //将2个函数放入一个数组当中
      const arr = [foo, bar];
      for (const fn of arr) {
        console.log(fn.name);
      }
      //2.length属性,在函数当中接收参数的个数默认值不算
      console.log(foo.length); //2 当前参数设置了默认值 c=5
      console.log(bar.length); //2

      //额外补充
      // function test(...arr){
      //   console.log(arr)
      // }
      // test(1321321354,4654,6,46,54)

四、浏览器渲染原理

1.网页的解析过程

大家有没有深入思考过:一个网页URL从输入到浏览器中,到显示经历过怎么样的解析过程呢?
要想深入理解下载的过程,我们还要先理解,一个index.html被下载下来后是如何被解析和显示在浏览器上的.

2..浏览器的内核

        ◼常见的浏览器内核有
Trident (三叉戟): IE 360 安全浏览器、搜狗高速浏览器、百度浏览器、 UC 浏览器;
Gecko (壁虎): Mozilla Firefox
Presto (急板乐曲) -> Blink (眨眼): Opera
Webkit Safari 360 极速浏览器、搜狗高速浏览器、移动端浏览器( Android iOS
Webkit -> Blink Google Chrome Edge
我们经常说的浏览器内核指的是浏览器的排版引擎:
排版引擎 (layout engine),也称为 浏览器引擎 (browser engine)、 页面渲染引擎 (rendering engine)或 样版引擎
也就是一个网页下载下来后,就是由我们的渲染引擎来帮助我们解析的。

 

在渲染树( Render Tree )上运行 布局( Layout 以计算每个节点的几何体。
渲染树会表示显示哪些节点以及其他样式,但是 不表示每个节点的尺寸、位置 等信息;
布局是确定呈现树中 所有节点的宽度、高度和位置信息
第五步是将每个节点绘制( Paint )到屏幕上
在绘制阶段,浏览器将布局阶段计算的 每个 frame 转为屏幕上实际的像素点
包括 将元素的可见部分进行绘制 ,比如 文本、颜色、边框、阴影、替换元素(比如 img

3.回流和重绘

理解回流 reflow :(也可以称之为重排)
第一次确定节点的大小和位置,称之为布局( layout )。
之后对节点的大小、位置修改重新计算称之为回流。
什么情况下引起回流呢?
比如 DOM 结构发生改变(添加新的节点或者移除节点);
比如改变了布局(修改了 width height padding font-size 等值)
比如窗口 resize (修改了窗口的尺寸等)
比如调用 getComputedStyle 方法获取尺寸、位置信息;
理解重绘 repaint
第一次渲染内容称之为绘制( paint )。
之后重新渲染称之为重绘。
什么情况下会引起重绘呢?
比如修改背景色、文字颜色、边框颜色、样式等;
回流一定会引起重绘,所以回流是一件很消耗性能的
事情。
所以在开发中要尽量避免发生回流:
1. 修改样式时 尽量一次性修改
比如通过 cssText 修改,比如通过添加 class 修改
2. 尽量 避免频繁的操作 DOM
我们可以在一个 DocumentFragment 或者父元素中
将要操作的 DOM 操作完成,再一次性的操作;
3. 尽量 避免通过 getComputedStyle 获取尺寸、位置 等信
息;
4. 某些元素使用 position absolute 或者 fixed
并不是不会引起回流,而是开销相对较小,不会对 其他元素造成影响。

特殊解析 – composite合成

绘制的过程,可以将布局后的元素绘制到多个合成图层中。
这是浏览器的一种优化手段;
默认情况下,标准流中的内容都是被绘制在同一个图层(Layer)中的;
而一些特殊的属性,会创建一个新的合成层( CompositingLayer ),并且新的图层可以利用GPU来加速绘制;
因为 每个合成层都是单独渲染 的;
那么哪些属性可以形成新的合成层呢?常见的一些属性:
3D transforms (使用过度动画时才会不在一个图层)
video、canvas、iframe
opacity 动画转换时;
position: fixed
will-change :一个实验性的属性,提前告诉浏览器元素可能发生哪些变化;
animation 或 transition 设置了opacity、transform
分层确实可以提高性能,但是它以内存管理为代价,因此不应作为 web 性能优化策略的一部分过度使用。

script元素和页面解析的关系

我们现在已经知道了页面的渲染过程,但是JavaScript在哪里呢?
事实上,浏览器在解析HTML的过程中,遇到了 script元素是不能继续构建DOM树 的;
它会 停止继续构建,首先下载JavaScript代码,并且执行JavaScript的脚本
只有 等到JavaScript脚本执行结束后,才会继续解析HTML,构建DOM树
为什么要这样做呢?
这是 因为JavaScript的作用之一就是操作DOM,并且可以修改DOM
如果我们 等到DOM树构建完成并且渲染再执行JavaScript,会造成严重的回流和重绘,影响页面的性能
所以会在 遇到script元素时,优先下载和执行JavaScript代码,再继续构建DOM 树;
但是这个也往往会带来新的问题,特别是现代页面开发中:
在目前的开发模式中(比如Vue、React), 脚本往往比HTML页面更“重”,处理时间需要更长
所以会 造成页面的解析阻塞,在脚本下载、执行完成之前,用户在界面上什么都看不到
为了解决这个问题,script元素给我们提供了两个属性(attribute):defer和async。

defer属性

defer 属性告诉浏览器 不要等待脚本下载 ,而 继续解析HTML,构建DOM Tree
脚本 会由浏览器来进行下载,但是不会阻塞DOM Tree 的构建过程;
如果脚本提前下载好了,它会 等待DOM Tree构建完成,在DOMContentLoaded事件之前先执行defer中的代码
所以DOMContentLoaded总是会等待defer中的代码先执行完成。
另外多个带defer的脚本是可以保持正确的顺序执行的。
从某种角度来说,defer可以提高页面的性能,并且推荐放到head元素中;
注意:defer仅适用于外部脚本,对于script默认内容会被忽略。

async属性

async 特性与 defer 有些类似,它也能够让脚本不阻塞页面。
async是让一个脚本完全独立的:
浏览器 不会因 async 脚本而阻塞 (与 defer 类似);
async脚本不能保证顺序,它是独立下载、独立运行,不会等待其他脚本
async不会能保证在DOMContentLoaded之前或者之后执行
defer通常用于需要在文档解析后操作DOM的JavaScript代码,并且对多个script文件有顺序要求的;
async通常用于独立的脚本,对其他脚本,甚至DOM没有依赖的;

五、声明变量的问题

var

1.允许重复的变量声明

      var a = 1;
      function print() {
        console.log(a);
      }
      print(); // Output: 1
      var a = 2;
      print(); // Output: 2

2.变量的提升:怪异的数据访问 闭包问题

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值