第五期

公众号同步

typeof运算符和instanceof运算符以及isPrototypeOf()方法的区别

typeof一般用来判断基础类型,他的返回值是一个字符串,返回值的值分别为:string、number、boolean、object、function、undefined;凡是用new创建的实例,返回的都是object。

instanceof一般用来判断某个实例是否属于某种类型;一般通过原型关系来判断。以下是常见的例子:

alert(Object instanceof Object);//true
alert(Function instanceof Function);//true
    
alert(Function instanceof Object);//true
    
alert(String instanceof String);//false
alert(Number instanceof Number);//false
alert(Boolean instanceof Boolean);//false

isPrototypeOf判断某对象是否是指定实例的原型;需要注意的是:该方法不会检查原型链中的对象。即:只检查本实例的直接原型,不会检查本实例的原型的原型。

function ClassA(){}
function ClassB(){}
function ClassC(){}
/**
  * ClassB的原型设为ClassA,ClassC的原型设为ClassB
  * 注意:这与原型继承不同
  */
ClassB.prototype = ClassA;
ClassC.prototype = ClassB;
var b = new ClassB();
var c = new ClassC();
alert(ClassA.isPrototypeOf(b));//true
alert(ClassB.isPrototypeOf(c));//true
alert(ClassA.isPrototypeOf(c));//false

描述以下变量的区别:null,undefined或undeclared

  • null 变量声明了然后赋值为null,是一个空的指针引用

  • undefined 是变量声明了但是还没有赋值,使用的时候就是undefined

  • undeclared 是没有声明,也就是没使用var关键字,如果赋值的话,会被创建于global object中,没赋值的话直接报语法错误,not defined。不同null,undefined是语言类型, undeclared是语法错误。

  • undeclared属于语法错误,'use strict' 下会避免此类语法错误。

BFC,IFC,FFC的区别

在了解上述区别的时候,先了解FC,FC是Formatting Context,格式化上下文。指页面中一个渲染区域,拥有一套渲染规则,它决定了其子元素如何定位,以及与其他元素的相互关系和作用。

BFC:Block块级格式化上下文,它会形成一个块,块里面的样式操作不会影响块外面的布局,一般用于防止外边距塌陷,浮动布局。

IFC:Inline行内格式化上下文,它没有固定的高度,高度是其包裹元素的最大高度,一般用来形成行内块级元素,用于线性排列。相关联的两个常用属性:inline-block、text-align、vertical-align。

GFC:Grid布局,网格布局,当display设置为grid的时候生效。

FFC:Flex自适应格式化上下文,伸缩布局,也叫弹性布局。

如何用网页脚本追踪用户

这个问题摘自阮一峰网络日志2019年4月15号如何用网页脚本追踪用户

目的是收集用户信息。

初始是通过延迟页面卸载来保证异步请求的成功。主要思路是延迟unload的执行;文中提到两个方法,一个耗时循环,两一个是setTimeout。

还有一种做法是反弹追踪,就是网页跳转时,先跳到一个或多个中间网址,以便收集信息,然后再跳转到原来的目标网址。

上面两种方式严重影响用户体验;浏览器专门实现了一个api来帮助我们,navigator.sendBeacon()方法可以保证,异步请求一定会发出。第一个参数是请求的网址,第二个参数是发送的数据。注意,Beacon API 发出的是 POST 请求。

还有一种方式是ping属性;HTML 的<a>标签有一个ping属性,只要用户点击,就会向该属性指定的网址,发出一个 POST 请求。ping属性无法指定数据体,似乎只能通过 URL 的查询字符串携带信息。

prototypeconstructorarguments.calleecaller分别是什么意思

prototype是函数的特性,一般用于构造函数,通过构造函数创建的实例,我们有两种添加属性的方式,一种是通过实例添加;另一种是通过构造函数添加;

function employee(name,job,born){
        this.name=name;
        this.job=job;
        this.born=born;
}

var bill=new employee("Bill Gates","Engineer",1985);

employee.prototype.salary=null;
bill.salary=20000;

通过实例添加的属性专属于当前实例,不会共享于其他通过此构造函数创建的实例;但是通过构造函数的prototype添加的属性会共享。

而且通过prototype添加的属性会直接添加到实例的__proto__下面。

constructor是实例的特性,它用来判断实例是通过哪个构造函数创建的,而且它是可以修改的,所以用它来判断不是很准确。

constructor还是class类中必不可少的属性,用于创建和初始化class创建的对象的特殊方法。用于定义类中的私有属性。

arguments.callee表示当前正在执行的函数,它常用在匿名函数中实现递归;

[1,2,3,4,5].map(function (n) {
    return !(n > 1) ? 1 : arguments.callee(n - 1) * n;
})

但它不是一个很好的解决方案,因为在严格模式下arguments会有限制,另一个原因是递归调用会获取到一个不同的 this 值。

var global = this;

var sillyFunction = function (recursed) {
    if (!recursed) { return arguments.callee(true); }
    if (this !== global) {
        alert("This is: " + this);
    } else {
        alert("This is the global");
    }
}

sillyFunction();

有一个很好的解决方式是使用内联函数:

var factorial = (function f(num) {
    if (num <= 1) {
        return 1;
    } else {
        return num*f(num - 1);
    }
});
console.log(factorial(5)); // 120
var anothorFactorial = factorial;

factorial = null;
console.log(anothorFactorial(5)); // 120

还有一种解决方案是尾递归:

function factorial(n, total) {
  if (n === 1) return total;
  return factorial(n - 1, n * total);
}

factorial(5, 1) // 120

由此可见,“尾调用优化”对递归操作意义重大,所以一些函数式编程语言将其写入了语言规格。ES6 亦是如此,第一次明确规定,所有 ECMAScript 的实现,都必须部署“尾调用优化”。这就是说,ES6 中只要使用尾递归,就不会发生栈溢出(或者层层递归造成的超时),相对节省内存。
ECMAScript 6 入门 --阮一峰

caller:返回调用指定函数的函数。 arguments.caller 已经废弃,但是你还可以使用 Function.caller。

我们常用caller来判断当前上下文。

如果一个函数f是在全局作用域内被调用的,则f.caller为null,相反,如果一个函数是在另外一个函数作用域内被调用的,则f.caller指向调用它的那个函数.

function myFunc() {
  if (myFunc.caller == null) {
      alert("该函数在全局作用域内被调用!");
  } else{
    alert("调用我的是函数是" + myFunc.caller);
  }
}
function f(){
    myFunc()
}
f()//"调用我的是函数是f"
myFunc()//该函数在全局作用域内被调用!