JavaScript
js数据类型以及如何判断数据类型
基本数据类型: string number boolean null undefined symbol
引用数据类型:object
判断数据类型常用的是typeof、instanceof(可以准确的判断对象的类型)作用域和作用域链
含义:定义变量的区域,有一套访问变量的规则,根据这规则来管理浏览器引擎如何在当前作用域和嵌套作用域根据变量进行变量查找
作用域的分类:全局作用域、函数作用域、块作用域
作用域链: 当js使用一个变量的时候,会先在当前作用域寻找该变量,如何当前作用域找不到,则回到上一级作用域去寻找,以此类推知道找到该变量this
在全局范围内, this指向的是window
函数中this指的是调用他的那个对象(箭头函数特殊)
构造函数中,this指向的是new出来的新对象
call, apply中,this被绑定在指定的对象
箭头函数中,this指向的是函数定义时作用域的this原型与原型链
4.1 构造函数
ES的构造函数其实就是能创建对象的函数,使用new调用的函数是构造函数,直接调用的则是普通函数1
2
3
4
5
6
7
8function Person(name, age, job) {
this.name = name;
this.age = age;
this.job = job;
this.sayName = function() { alert(this.name) }
}
var person1 = new Person('Zaxlct', 28, 'Software Engineer');
var person2 = new Person('Mick', 23, 'Doctor');person1和person2都是person的实例,这两个实例都有一个constructor(即构造函数),该属性指向Person,换句话说就是实例的构造函数属性指向构造函数
其实person1和person2也是Object的实例,因为所有自定义对象都是继承于Objectconstructor其实是用来标识对象类型的,但实际上一般使用instanceof来确定对象类型,所以如下表达式成立
1
2
3
4console.log(person1.constructor == Person); //true
console.log(person2.constructor == Person); //true
console.log(person1 instanceof Object); // true
console.log(person1 instanceof Person) // true4.2 原型对象
js中每定义一个对象(函数也是对象)时,对象中都会包含一些预先定义的属性,当定义函数对象时,就会被包含prototype属性,该属性指向函数的原型对象
Object.getPrototypeOf() 可以获得传入对象的原型对象1
2
3
4
5
6
7
8
9
10
11
12
13
14
15function Person() {}
Person.prototype.name = 'Zaxlct';
Person.prototype.age = 28;
Person.prototype.job = 'Software Engineer';
Person.prototype.sayName = function() {
alert(this.name);
}
var person1 = new Person();
person1.sayName(); // 'Zaxlct'
var person2 = new Person();
person2.sayName(); // 'Zaxlct'
console.log(person1.sayName == person2.sayName); //true每个对象都有__proto__属性,只有函数对象才有prototype属性, 但是Function.prototype除外
那什么是原型对象呢?看如下代码(Person.prototype指向原型对象)1
2
3
4
5
6
7
8Person.prototype = {
name: 'Zaxlct',
age: 28,
job: 'Software Engineer',
sayName: function() {
alert(this.name);
}
}原型对象其实就是一个普通对象,其实就是Person.prototype,甚至可以给其赋值 var a = Person.prototype,上述代码中给a添加四个属性name、age、job、sayName,但其实还有一个默认属性construcor
默认情况下,所有原型对象都会有一个构造函数(constructor)属性,这个属性指向prototype所在的函数(Person),并且这个元素是个指针,即 Person.prototype.constructor == Person
从之前的结论可以知道实例的构造函数指向构造函数, 即 person1.constructor == Person
换句话说,Person.prototype也是Person的实例(有待考证),即原型对象是构造函数的一个实例
实例只有指向原型的指针,没有指向构造函数的指针
重写构造函数的原型之后,在创建的实例才会引用新的原型
4.3 proto
js在创建对象的时候都有一个叫做proto的内置属性,用于指向创建他的构造函数的原型对象person1.proto== Person.prototype
4.4 构造器
我们可以使用 var obj = {}来创建一个对象,等同于var obj = new Object(),所以obj.constructor == Object
obj.proto == Object.prototype同理,创建对象的构造器不只是Object,也可以是Array,Date,Function等
这些构造器都是函数对象1
2
3
4
5
6
7
8
9
10
11var b = new Array();
b.constructor === Array;
b.__proto__ === Array.prototype;
var c = new Date();
c.constructor === Date;
c.__proto__ === Date.prototype;
var d = new Function();
d.constructor === Function;
d.__proto__ === Function.prototype;所有函数对象的_proto_都指向Function.prototype,它是一个空函数(Empty function)
所有对象的_proto_都指向其构造器的prototype
4.5 原型链person1.proto = Person.prototype
person1的_proto_等于person1构造函数的prototype
person1.constructor == PersonPerson.proto = Function.ptototype
Person._proto_等于Person.constructor的prototype,
Person.constructor = FunctionPerson.prototype.proto = Object.prototype
Person.prototype是一个普通对象,即Object.prototype
Object.proto = Function.prototype
object的构造函数是Function
Object.prototype.proto = null
Object.prototype 对象也有proto属性,为null,因为他处于原型链的顶端
4.6 函数对象
所有函数对象的_proto_(即所有的构造器)都指向Function.prototype,换句话说所有函数对象的构造函数都是Function
所有构造器都继承Function.prototype的属性及方法Function.prototype是唯一一个 typeof Function.prototype == Function的prototype
其他构造器的prototype都是object
普通对象.proto = Function.ptototype
Function.prototype.proto === Object.prototype
Object.prototype.proto === null4.7 原型链案例
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16function Person() {}
var person1 = new Person()
person1.__proto__ === Person.prototype
Person.__proto__ == Function.prototype // 所有函数对象的__proto__都指向Function.prototype
Person.prototype._proto_ == Object.prototype
Function.__proto__ == Function.prototype // Function也属于函数对象
Function.prototype.__proto__ == Object.prototype
Object.prototype.__proto__== null
var num = new Array()
num.__proto__ == Array.prototype
Array.prototype.__proto__ == Object.prototype
Object.prototype.__proto__ == null
Array.__proto__ = Function.prototype4.8 hasOwnProperty、in、for in的用法
hasOwnProperty() 方法用于确定某个属性是否存在于实例中,存在则返回true
in 可以确定某属性是否存在于原型或实例上,只要存在就返回true
因此只要hasOwnProperty()返回false, in 返回true,就说明该属性是原型属性
在 for-in 循环中使用 in 操作符时,可以通过对象访问且可以被枚举的属性都会返回,包括实例属性和原型属性
获得对象上所有可枚举的实例属性,使用Object.keys(obj)
列出所有实例属性(包含不可枚举的)可使用Object.getOwnPropertyNames()4.9 ES2017新增Object.values()和Object.entries()
Object.value()返回对象值的数组
Object.entries()返回对象键值对的数组1
2
3
4
5
6
7
8const o = {
foo: 'bar'
,
baz: 1,
qux: {}
};
console.log(Object.values(o)); // ["bar", 1, {}]
console.log(Object.entries((o))); // [["foo","bar"], ["baz", 1], ["qux", {}]]4.10 原型链的问题
原型中包含的引用值会在所有实例间共享
1
2
3
4
5
6
7
8
9
10
11function SuperType() {
this.colors = ["red" , "blue" , "green"];
}
function SubType() {}
// 继承SuperType
SubType.prototype = new SuperType();
let instance1 = new SubType();
instance1.colors.push("black");
console.log(instance1.colors); // "red,blue,green,black"
let instance2 = new SubType();
console.log(instance2.colors); // "red,blue,green,black"子类型在实例化时不能给父类型的构造函数传参
4.11 盗用构造函数
为解决原型包含引用值导致的继承问题,“盗用构造函数”流行了起来,也叫“对象伪装”,“经典继承”
基本思路:在子类构造函数中通过call/apply调用父类构造函数1
2
3
4
5
6
7
8
9
10
11
12function SuperType() {
this.colors = ["red","blue","green"];
}
function SubType() {
// 继承SuperType
SuperType.call(this);
}
let instance1 = new SubType();
instance1.colors.push("black");
console.log(instance1.colors); // "red,blue,green,black"
let instance2 = new SubType();
console.log(instance2.colors); // "red,blue,green"优点:在子类构造函数中向父类构造函数传参
存在的问题:必须在构造函数中定义方法,因此函数不能重用
4.12 new做了什么- 创建了一个全新的对象
- 该对象会被执行[prototype]链接
- 生成的新对象会绑定到函数调用的this
- 通过new创建的对象最终都会被[prototype]链接到该函数的原型(也就是prototype)对象上
- 如果函数没有返回对象Object(包含Function、Array等),那么new表达式中的函数调用会自动返回这个新函数