JS知识点

2020-04-12  本文已影响0人  吃肉肉不吃肉肉

1.事件冒泡与事件捕获

事件冒泡:由最具体的元素(目标元素)向外传播到最不具体的元素
事件捕获:由最不确定的元素到目标元素

2.复杂数据类型如何转变为字符串

3.javascript 中 this 的指向问题

1、全局环境

全局环境下,this 始终指向全局对象(window),无论是否严格模式;

// 在浏览器中,全局对象为 window 对象:
console.log(this === window); // true

this.a = 37;
console.log(window.a); // 37

2、函数上下文调用
function f1() {
  return this;
}
f1() === window; // true

(2)严格模式下,this 指向 undefined。

function f2() {
  "use strict"; // 这里是严格模式
  return this;
}
f2() === undefined; // true

//方式1
var o = {
  prop: 37,
  f: function() {
    return this.prop;
  }
};
//当 o.f()被调用时,函数内的this将绑定到o对象。
console.log(o.f()); // logs 37

//方式2
var o = { prop: 37 };
function independent() {
  return this.prop;
}
//函数f作为o的成员方法调用
o.f = independent;
console.log(o.f()); // logs 37

//方式3
//this 的绑定只受最靠近的成员引用的影响
o.b = { g: independent, prop: 42 };
console.log(o.b.g()); // 42

特殊例子

// 例子1
var o = {
  a: 10,
  b: {
    // a:12,
    fn: function() {
      console.log(this.a); //undefined
      console.log(this); //{fn: ƒ}
    }
  }
};
o.b.fn();

// 例子2
var o = {
  a: 10,
  b: {
    a: 12,
    fn: function() {
      console.log(this.a); //undefined
      console.log(this); //window
    }
  }
};
var j = o.b.fn;
j();
// this永远指向的是最后调用它的对象,也就是看它执行的时候是谁调用的,例子2中虽然函数fn是被对象b所引用,但是在将fn赋值给变量j的时候并没有执行所以最终指向的是window,这和例子1是不一样的,例子1是直接执行了fn

var o = {
  f: function() {
    return this.a + this.b;
  }
};
var p = Object.create(o);
p.a = 1;
p.b = 4;

console.log(p.f()); // 5

上述例子中,对象 p 没有属于它自己的 f 属性,它的 f 属性继承自它的原型。当执行 p.f()时,会查找 p 的原型链,找到 f 函数并执行。因为 f 是作为 p 的方法调用的,所以函数中的 this 指向 p。
(2)相同的概念也适用于当函数在一个 getter 或者 setter 中被调用。用作 getter 或 setter 的函数都会把 this 绑定到设置或获取属性的对象。
(3)call()和 apply()方法:当函数通过 Function 对象的原型中继承的方法 call() 和 apply() 方法调用时, 其函数内部的 this 值可绑定到 call() & apply() 方法指定的第一个对象上, 如果第一个参数不是对象,JavaScript 内部会尝试将其转换成对象然后指向它。

function add(c, d) {
  return this.a + this.b + c + d;
}
var o = { a: 1, b: 3 };

add.call(o, 5, 7); // 1 + 3 + 5 + 7 = 16
add.apply(o, [10, 20]); // 1 + 3 + 10 + 20 = 34

function tt() {
  console.log(this);
}
// 第一个参数不是对象,JavaScript内部会尝试将其转换成对象然后指向它。
tt.call(5); // 内部转成 Number {[[PrimitiveValue]]: 5}
tt.call("asd");
 // 内部转成 String {0: "a", 1: "s", 2: "d", length: 3, [[PrimitiveValue]]: "asd"}

(4)bind()方法:由 ES5 引入, 在 Function 的原型链上, Function.prototype.bind
通过 bind 方法绑定后, 函数将被永远绑定在其第一个参数对象上, 而无论其在什么情况下被调用。

function f() {
  return this.a;
}

var g = f.bind({ a: "azerty" });
console.log(g()); // azerty

var o = { a: 37, f: f, g: g };
console.log(o.f(), o.g()); // 37, azerty2.4 

构造函数中的 this当一个函数用作构造函数时(使用 new 关键字),它的 this 被绑定到正在构造的新对象。构造器返回的默认值是 this 所指的那个对象,也可以手动返回其他的对象。

function C() {
  this.a = 37;
}

var o = new C();
console.log(o.a); // 37
// 为什么this会指向o?
//首先new关键字会创建一个空的对象,然后会自动调用一个函数apply方法,将this指向这个空对象,这样的话函数内部的this就会被这个空的对象替代。

function C2() {
  this.a = 37;
  return { a: 38 }; // 手动设置返回{a:38}对象
}

o = new C2();
console.log(o.a); // 38

特殊例子
当 this 碰到 return 时

// 例子1
function fn() {
  this.user = "追梦子";
  return {};
}
var a = new fn();
console.log(a.user); //undefined
// 例子2
function fn() {
  this.user = "追梦子";
  return function() {};
}
var a = new fn();
console.log(a.user); //undefined
// 例子3
function fn() {
  this.user = "追梦子";
  return 1;
}
var a = new fn();
console.log(a.user); //追梦子
// 例子4
function fn() {
  this.user = "追梦子";
  return undefined;
}
var a = new fn();
console.log(a.user); //追梦子
// 例子5
function fn() {
  this.user = "追梦子";
  return undefined;
}
var a = new fn();
console.log(a); //fn {user: "追梦子"}
// 例子6
// 虽然null也是对象,但是在这里this还是指向那个函数的实例,因为null比较特殊
function fn() {
  this.user = "追梦子";
  return null;
}
var a = new fn();
console.log(a.user); //追梦子

// 总结:如果返回值是一个对象,那么this指向的就是那个返回的对象,如果返回值不是一个对象那么this还是指向函数的实例。

//默认情况下代码
function Person() {
  this.age = 0;
  setTimeout(function() {
    console.log(this);
  }, 3000);
}

var p = new Person(); //3秒后返回 window 对象
//通过bind绑定
function Person() {
  this.age = 0;
  setTimeout(
    function() {
      console.log(this);
    }.bind(this),
    3000
  );
}

var p = new Person(); //3秒后返回构造函数新生成的对象 Person{...}

3、在 DOM 事件中
// 被调用时,将关联的元素变成蓝色
function bluify(e) {
  //this指向所点击元素
  console.log("this === e.currentTarget", this === e.currentTarget); // 总是 true
  // 当 currentTarget 和 target 是同一个对象时为 true
  console.log("this === e.target", this === e.target);
  this.style.backgroundColor = "#A5D9F3";
}

// 获取文档中的所有元素的列表
var elements = document.getElementsByTagName("*");

// 将bluify作为元素的点击监听函数,当元素被点击时,就会变成蓝色
for (var i = 0; i ‹ elements.length; i++) {
  elements[i].addEventListener("click", bluify, false);
}

‹button onclick="console.log(this)"›show me‹/button›
‹button onclick="(function () {console.log(this)})()"›show inner this‹/button›
‹button onclick="(function () {'use strict'; console.log(this)})()"›
  use strict
‹/button›

// 控制台打印
‹button onclick="console.log(this)"›show me‹/button›
Window {postMessage: ƒ, blur: ƒ, focus: ƒ, close: ƒ, parent: Window, …}
undefined

4、箭头函数
var globalObject = this;
var foo = () =› this;
console.log(foo() === globalObject); // true

//1、箭头函数在函数内部,以非方法的方法使用
function Person() {
  this.age = 0;
  setInterval(() =› {
    this.age++;
  }, 3000);
}
var p = new Person(); //Person{age: 0}

//普通函数作为内部函数
function Person() {
  this.age = 0;
  setInterval(function() {
    console.log(this);
    this.age++;
  }, 3000);
}
var p = new Person(); //Window{...}

在 setTimeout 中的 this 指向了构造函数新生成的对象,而普通函数指向了全局 window 对象。

var obj = {
  i: 10,
  b: () =› console.log(this.i, this),
  c: function() {
    console.log(this.i, this);
  }
};
obj.b(); // undefined window{...}
obj.c(); // 10 Object {...}

var adder = {
  base: 1,
  //对象的方法内部定义箭头函数,this是箭头函数所在的作用域的this,
  //而方法add的this指向adder对象,所以箭头函数的this也指向adder对象。
  add: function(a) {
    var f = v =› v + this.base;
    return f(a);
  },
  //普通函数f1的this指向window
  add1: function() {
    var f1 = function() {
      console.log(this);
    };
    return f1();
  },
  addThruCall: function inFun(a) {
    var f = v =› v + this.base;
    var b = {
      base: 2
    };

    return f.call(b, a);
  }
};

console.log(adder.add(1)); // 输出 2
adder.add1(); //输出全局对象 window{...}
console.log(adder.addThruCall(1)); // 仍然输出 2(而不是3,其内部的this并没有因为call() 而改变,其this值仍然为函数inFun的this值,指向对象adder

var handler = {
  id: "123456",

  init: function() {
    document.addEventListener(
      "click",
      event =› this.doSomething(event.type),
      false
    );
  },

  doSomething: function(type) {
    console.log("Handling " + type + " for " + this.id);
  }
};

上面代码的 init 方法中,使用了箭头函数,这导致这个箭头函数里面的 this,总是指向 handler 对象。如果不使用箭头函数则指向全局 document 对象。

//例1,this指向定义箭头函数所在的作用域,它位于对象cat内,但cat不能构成一个作用域,所以指向全局window,改成普通函数后this指向cat对象。
const cat = {
  lives: 9,
  jumps: () =› {
    this.lives--;
  }
};

//例2,此时this也是指向window,不能动态监听button,改成普通函数后this指向按钮对象。
var button = document.getElementById("press");
button.addEventListener("click", () =› {
  this.classList.toggle("on");
});

4.ES6 都有什么 Iterator 遍历器

SetMap
解析:

  1. 遍历器(Iterator)是一种接口,为各种不同的数据结构提供统一的访问机制。任何数据结构只要部署 Iterator 接口,就可以完成遍历操作(即依次处理该数据结构的所有成员)
  2. Iterator 的作用有三个:
  1. 默认部署了 Iterator 的数据有 ArrayMapSetStringTypedArrayargumentsNodeList 对象,ES6 中有的是 Set、Map**、

5. ES6 中类的定义

// 1、类的基本定义
class Parent {
  constructor(name = "小白") {
    this.name = name;
  }
}

// 2、生成一个实例
let g_parent = new Parent();
console.log(g_parent); //{name: "小白"}
let v_parent = new Parent("v"); // 'v'就是构造函数name属性 , 覆盖构造函数的name属性值
console.log(v_parent); // {name: "v"}

// 3、继承
class Parent {
  //定义一个类
  constructor(name = "小白") {
    this.name = name;
  }
}

class Child extends Parent {}

console.log("继承", new Child()); // 继承 {name: "小白"}

// 4、继承传递参数
class Parent {
  //定义一个类
  constructor(name = "小白") {
    this.name = name;
  }
}

class Child extends Parent {
  constructor(name = "child") {
    // 子类重写name属性值
    super(name); // 子类向父类修改 super一定放第一行
    this.type = "preson";
  }
}
console.log("继承", new Child("hello")); // 带参数覆盖默认值  继承{name: "hello", type: "preson"}

// 5、ES6重新定义的ES5中的访问器属性
class Parent {
  //定义一个类
  constructor(name = "小白") {
    this.name = name;
  }

  get longName() {
    // 属性
    return "mk" + this.name;
  }

  set longName(value) {
    this.name = value;
  }
}

let v = new Parent();
console.log("getter", v.longName); // getter mk小白

v.longName = "hello";
console.log("setter", v.longName); // setter mkhello// 

6、类的静态方法
class Parent {
  //定义一个类
  constructor(name = "小白") {
    this.name = name;
  }

  static tell() {
    // 静态方法:通过类去调用,而不是实例
    console.log("tell");
  }
}

Parent.tell(); // tell// 

7、类的静态属性:

class Parent {
  //定义一个类
  constructor(name = "小白") {
    this.name = name;
  }

  static tell() {
    // 静态方法:通过类去调用,而不是实例
    console.log("tell"); // tell
  }
}

Parent.type = "test"; // 定义静态属性

console.log("静态属性", Parent.type); // 静态属性 test

let v_parent = new Parent();
console.log(v_parent); // {name: "小白"}  没有tell方法和type属性

6.说说你对 promise 的了解

Promise 是异步编程的一种解决方案,比传统的解决方案——回调函数和事件监听——更合理和更强大。
所谓 Promise,简单说就是一个容器,里面保存着某个未来才会结束的事件(通常是一个异步操作)的结果。从语法上说,Promise 是一个对象,从它可以获取异步操作的消息。Promise 提供统一的 API,各种异步操作都可以用同样的方法进行处理。Promise 对象有以下两个特点:

  1. 对象的状态不受外界影响,Promise 对象代表一个异步操作,有三种状态:Pending(进行中)、Resolved(已完成,又称 Fulfilled)和 Rejected(已失败)
  2. 一旦状态改变,就不会再变,任何时候都可以得到这个结果。

7.Set 数据结构

es6 方法,

Set 本身是一个构造函数,它类似于数组,但是成员值都是唯一的。

const set = new Set([1, 2, 3, 4, 4]);
console.log([...set]); // [1,2,3,4]
console.log(Array.from(new Set([2, 3, 3, 5, 6]))); //[2,3,5,6]

8.箭头函数需要注意的地方

箭头函数有几个使用注意点。

上面四点中,第一点尤其值得注意。this 对象的指向是可变的,但是在箭头函数中,它是固定的。

function foo() {
  setTimeout(() =› {
    console.log("id:", this.id);
  }, 100);
}

var id = 21;

foo.call({ id: 42 });
// id: 42

9.ES6 如何动态加载 import

import("lodash").then(_ =› {
  // Do something with lodash (a.k.a '_')...
});

10.ECMAScript6 怎么写class么,为什么会出现class这种东西?

class Point {
  constructor(x, y) {
    this.x = x;
    this.y = y;
  }
  toString() {
     return '('+this.x+', '+this.y+')';
  }
}

11.自执行函数?用于什么场景?好处?

自执行函数:
1、声明一个匿名函数
2、马上调用这个匿名函数。
作用:创建一个独立的作用域。
好处:防止变量弥散到全局,以免各种 js 库冲突。隔离作用域避免污染,或者截断作用域链,避免闭包造成引用变量无法释放。利用立即执行特性,返回需要的业务函数或对象,避免每次通过条件判断来处理场景:一般用于框架、插件等场景

12.多个页面之间如何进行通信

13.css 动画和 js 动画的差异

14.数组方法 pop() push() unshift() shift()

15.事件绑定与普通事件有什么区别

用普通事件添加相同事件,下面会覆盖上面的,而事件绑定不会

普通事件是针对非 dom 元素,事件绑定是针对 dom 元素的事件

16. IE 和 DOM 事件流的区别

1.事件流的区别

IE 采用冒泡型事件 Netscape 使用捕获型事件
DOM 使用先捕获后冒泡型事件
示例:

‹body›
  ‹div›
    ‹button›点击这里‹/button›
  ‹/div›
‹/body›

冒泡型事件模型: button-›div-›body (IE 事件流)
捕获型事件模型: body-›div-›button (Netscape 事件流)
DOM 事件模型: body-›div-›button-›button-›div-›body (先捕获后冒泡)

2.事件侦听函数的区别

IE 使用:

[Object].attachEvent("name_of_event_handler", fnHandler); //绑定函数
[Object].detachEvent("name_of_event_handler", fnHandler); //移除绑定

DOM 使用:

[Object].addEventListener("name_of_event", fnHandler, bCapture); //绑定函数
[Object].removeEventListener("name_of_event", fnHandler, bCapture);
 //移除绑定bCapture 参数用于设置事件绑定的阶段,true 为捕获阶段,false 为冒泡阶段。

17.变量提升

js 代码执行的过程
  1. 变量提升
  2. 代码从上到下依次执行
    var 关键字和 function 关键字声明的变量会进行变量提升
变量提升发生的环境:

发生在代码所处的当前作用域。
变量提升

  1. var 关键字进行的变量提升,会把变量提前声明,但是不会提前赋值 。
  2. function 关键字对变量进行变量提升,既会把变量提前声明,又会把变量提前赋值,也就是把整个函数体提升到代码的顶部
  3. 有一些代码是不会执行的但是仍旧会发生变量提升,规则适用于 1,2
  1. 有一些代码是不会执行但是仍旧会发生变量提升,但是规则要发生变化
// 如果一个变量声明了但是未赋值,那么输出这个变量就会输出 undefined
var num;
console.log(num);

// 如果一个变量没有声明也没有赋值,那么就会报一个错:
console.log(num);
 // 输出一个不存在的变量 Uncaught ReferenceError: num is not defined

// var 关键字进行的变量提升
console.log(num);
var num = 123;
console.log(num);
var num = 456;
console.log(num);

// 变量提升之后的代码:
var num;
console.log(num);
num = 123;
console.log(num);
num = 456;
console.log(num);

// function 关键字的变量提升
console.log(fn);
function fn() {
  console.log(1);
}

// 变量提升之后的代码:
function fn() {
  console.log(1);
}
console.log(fn); // 输出fn的函数体

// 3.1 return 之后的代码依旧会发生变量提升  规则适用于1,2
function fn() {
  console.log(num);
  return;
  var num = 123;
}
fn();

// 变量提升之后的代码:
function fn() {
  var num;
  console.log(num);
  return;
  num = 123;
}
fn(); // undefined

function fn() {
  console.log(fo);
  return;
  function fo() {}
}
fn();

// 变量提升之后的代码:
function fn() {
  function fo() {}
  console.log(fo);
  return;
}
fn(); //输出fo的函数体

//3.2 代码报错之后的代码依旧会进行变量提升,规则适用于1,2
console.log(num);
xsasfgdsfqdfsdf; //报一个错
var num = 123;
console.log(num);

// 变量提升之后的代码:
var num;
console.log(num); //输出 undefined
dsagdsqghdwfh; // 报一个错误 ,错误之后的代码不会被执行
num = 123;
console.log(num);//function 关键字
console.log(fn);
sasgfdhwhsdqg;
function fn() {}
console.log(fn);

// 变量提升之后的代码:
function fn() {}
console.log(fn); // 输出 fn 的函数体
asdgsdgdfgfdg; // 报一个错误,报错之后的代码不会被执行
console.log(fn);

//4 代码不执行,但是会进行变量提升,不过规则不适用于1,2
//4.1 if判断语句
console.log(num);
if (false) {
    var num = 123;
}
console.log(num)

//  变量提升之后的代码:
var num;
console.log(num); //undefined
if (false) {
    num = 123;
}
console.log(num) //undefined

console.log(fn);
if (false) {
    function fn() {}
}
console.log(fn);

// 变量提升之后的代码:
var fn;
function fn;
console.log(fn) //undefined
if (false) {
    function fn() {}
}
console.log(fn) //undefined
if(false){
  function fn() {}
}
console.log(fn) //undefined

// try catch
try {
  console.log(num);
} catch (e) {
  var num = 123;
}
console.log(num);

var num;
try {
  console.log(num); // undefined
} catch (e) {
  num = 123;
}
console.log(num); // undefined

try {
  console.log(fn);
} catch (e) {
  function fn() {}
}
console.log(fn);

var fn;
try {
  console.log(fn); // undefined
} catch (e) {
  num = 123;
}
console.log(fn); // undefined

18.如何阻止冒泡与默认行为

阻止冒泡行为:
非 IE 浏览器 stopPropagation(),IE 浏览器 window.event.cancelBubble = true
阻止默认行为:
非 IE 浏览器 preventDefault(),IE 浏览器 window.event.returnValue = false
解析:
当需要阻止冒泡行为时,可以使用

function stopBubble(e) {
  //如果提供了事件对象,则这是一个非IE浏览器
  if (e && e.stopPropagation)
    //因此它支持W3C的stopPropagation()方法
    e.stopPropagation();
  //否则,我们需要使用IE的方式来取消事件冒泡
  else window.event.cancelBubble = true;
}

当需要阻止默认行为时,可以使用

//阻止浏览器的默认行为
function stopDefault(e) {
  //阻止默认浏览器动作(W3C)
  if (e && e.preventDefault) e.preventDefault();
  //IE中阻止函数器默认动作的方式
  else window.event.returnValue = false;
  return false;
}

19.js 中 this 闭包 作用域

this:指向调用上下文
闭包:定义一个函数就开辟了一个局部作用域,整个 js 执行环境有一个全局作用域
作用域:一个函数可以访问其他函数中的变量(闭包是一个受保护的变量空间)

var f = (function fn() {
  var name = 1;
  return function () {
    name++;
    console.log(name)
  }
})()

20.javascript 的同源策略

一段脚本只能读取来自于同一来源的窗口和文档的属性

解析:
同源策略:限制从一个源加载的文档或脚本如何与来自另一个源的资源进行交互。这是一个用于隔离潜在恶意文件的关键的安全机制。(来自 MDN 官方的解释)
简单来说就是:一段脚本只能读取来自于同一来源的窗口和文档的属性,这里的同一来源指的是主机名、协议和端口号的组合
具体解释:
(1)源包括三个部分:协议、域名、端口(http 协议的默认端口是 80)。如果有任何一个部分不同,则源不同,那就是跨域了。
(2)限制:这个源的文档没有权利去操作另一个源的文档。这个限制体现在:(要记住)CookieLocalStorageIndexDB 无法获取。无法获取和操作 DOM。不能发送 Ajax 请求。我们要注意,Ajax 只适合同源的通信
同源策略带来的麻烦:ajax 在不同域名下的请求无法实现,需要进行跨域操作

上一篇下一篇

猜你喜欢

热点阅读