简单数据类型:Undefined、Null、String、Number、Boolean、Symbol 复杂数据类型:Object // Undefined:声明,但未初始化 // Null:空对象指针
typeof操做符(检测基本数据类型):javascript
typeof的返回值有哪些: 1. undefined // 声明和未初始化的变量,使用typeof都会返回Undefined 2. boolean 3. string 4. number 5. object // 当是object、null、array时 6. function // 函数是对象,不是一种数据类型,由于特殊,typeof把它从对象中区分出来。typeof 正则也返回function)
typeof
的用途是检测基本数据类型,检测引用类型数的值时,用instanceof
java
instanceof操做符(肯定实例和原型之间关系):数组
若是变量是给定引用类型的实例
,instanceof
操做符就会返回true
例如:app
person instanceof Object arr instanceof Array pattern instanceof RegExp
(经典问题)判断一个对象是否是数组:函数
function createPerson(name, age) { var o = new Object() // 显示地建立对象 o.name = name o.age = age o.getName = function () { console.log(this.name) } return o // 最后须要return }
其实就是写了一个函数,每次建立对象就是调用这个函数。this
优势:工厂函数解决了建立多个相似对象的问题
缺点:没有解决对象识别问题(即怎样知道一个对象的类型 constructor)
构造函数能够建立特定类型的函数,像Object、Array这样的原生构造函数。咱们能够建立自定义的构造函数prototype
function Person(name, age) { this.name = name this.age = age this.getName = function() { console.log(this.name) } } var person1 = new Person('zhangsan', 18) var person2 = new Person('lisi', 20)
new 操做符作了什么: 1.建立一个新对象 2.将构造函数的做用于赋给新对象(所以this就只想新对象) 3.执行构造函数中的代码(为这个新对象添加属性) 4.返回新对象
person1和person2分别保存着Person的一个不一样实例
,这两个对象都有一个constructor
(构造函数)属性,该属性指向Person指针
person1.constructor == Person // true person2.constructor == Person // true
对象的constructor
属性最初是用来标识对象类别的。检测对象类型仍是用instanceof
更靠谱(++肯定实例和原型之间关系++)code
person1 instance Person //true person1 instance Object //true
构造函数还能够在另外一个对象的做用域中调用对象
var o = new Object() Person.call(o, 'xiaoming', 12) // 在o的做用于调用Person构造函数,o就拥有了Person全部的属性和方法。 o.getName() // 'xiaoming'
call()
和apply()
的第一个参数是谁,就是在谁的做用于中调用构造函数。
优势:(解决了对象识别问题)建立自定义的构造函数意味着未来能够将它的实例标识为一种特定的类型(Person类),这也是构造函数模式赛过工厂模式的地方
缺点:构造函数的每一个方法,都要在每一个实例上从新建立一遍。所以不一样实例上的同名函数不相等。
person1.getName == person2.getName // false
先理解一些概念:
咱们建立的每一个函数(例如构造函数)都有一个prototype
(原型)属性,这个属性是一个指针
,指向一个对象(函数的原型对象),这个对象包含全部实例共享的属性和方法
。prototype
就是经过调用构造函数而建立的那个对象实例的原型对象
。使用原型对象的好处是可让全部对象实例共享它所包含的属性和方法。
例如:给构造函数Person的原型添加属性和方法,那实例也会共享这些属性和方法。
function Person() {} Person.prototype.name = 'zhangsan' Person.prototype.getName = function() {console.log(this.name)} var person1 = new Person() person1.getName() // 'zhangsan'
每一个函数都有一个prototype属性,指向该函数的原型对象。而原型对象又有一个constructor(构造函数)属性,这个属性是一个指向prototype属性所在函数的指针。
例如:Person.prototype.constructor 指向 Person
当调用构造函数建立一个实例,该实例的内部将包含一个指针_proto_
,指向构造函数的原型对象
。这个链接存在于实例与构造函数的原型对象之间
,而不是存在于实例和构造函数之间。
Person.prototype.constructor-->Person 实例person1._proto_-->Person.prototype
Person.prototype.isPrototypeOf(person1) //true Object.getPrototypeOf(person1) == Person.prototype //true
hasOwnProperty
和in
:
hasOwnProperty
检测属性存在于实例,仍是原型上。只有属性值存在于实例时,才返回true in
操做符没法检测属性存在于实例仍是原型上。只要经过对象能访问到属性值,就返回true
function Person() {} Person.prototype.name = 'zhangsan' var person1 = new Person() person1.sex = '男' person1.hasOwnProperty('name') //false person1.hanOwnProperty('sex') //true name in person1 //true sex in person1 // true
判断属性仅存在于原型:
function(obj, name) { return !obj.hasOwnProperty(name) && (name in obj) }
for in
和Object.keys
for in // 实例和原型上全部可枚举的属性(返回的是全部可以经过对象访问的,可枚举的属性) Object.keys // 仅实例上可枚举的属性
重写原型(从新设定constructor):
Person.prototype = { constructor: Person, name: 'zhangsan', getName: function() { console.log(this.name) } }
优势:共享函数,不须要每次建立实例都从新建立同名函数。
缺点:属性的共享
建立自定义类型最经常使用的方式,使用最广范、承认度最高
集两种模式之长:构造函数模式
用于定义实例属性,原型模式
用于定义方法和共享的属性。这样每一个实例都有本身的一份实例属性副本,但同时又共享着对方法的引用,最大限度地节省了内存。
在没有类的状况下,能够采用如下方式建立对象。
组合使用构造函数模式和原型模式时,使用构造函数定义实例属性,使用原型定义共享的属性和方法
。主要依靠原型链
来实现继承
原型链的主要思想:利用原型让一个引用类型继承另外一个引用类型的属性和方法
先回顾下构造函数、原型和实例的关系:
每个构造函数都有一个原型对象(prototype),原型对象都包含一个指向构造函数的指针(constructor),而实例都包含一个指向原型对象的内部指针。
那么,假如咱们让原型对象等于另外一个类型的实例:
function SuperType() { this.type = true } SuperType.prototype.getSuperValue = function() { return this.type } function SubType() { this.subtype = false } // 继承了SuperType SubType.prototype = new SuperType() //实例赋值给原型的方式 SubType.prototype.getSubValue() { return this.subtype } var instance = new SubType() console.log(instance.getSuperValue()) // true
以上定义了两个类型:SuperType
和SubType
。每一个类型分别有一个属性和方法。SubType
继承了SuperType
,而继承是经过建立SuperType
实例,并将该实例赋给SubType.prototype
来实现的(以一个新类型的实例重写原型对象)。所以,原来存在于SuperType
的实例中的全部属性和方法,如今也存在于SubType.prototype
中了。
最终:instance
指向SubType
的原型,SubType
的原型又指向SuperType
的原型
当访问实例属性时,首先在示例中搜索该属性,若是没找到该属性,则会继续搜索实力的原型,若是还没找到,就沿着原型链继续往上找。
原型链的问题:
- 原型属性会被全部实例所共享。此方法实现继承,原型实际上变成了另外一个类型的实例。SuperType的属性就变成了SubType原型上的属性了,就会被SubType的实例instance一、instance2等所继承。
- 在建立子类型实例时,不能向超类型实例传递参数。
所以实际中不多单独使用原型链。
function SuperType() { this.colors = ['red', 'blue'] } function SubType() {} SubType.prototype = new SuperType() // 继承了SuperType var instance1 = new SubType() instance1.colors.push('black') console.log(instance.coloes) // 'red', 'blue', 'black' var instance2 = new SubType() console.log(instance2.colors) // 'red', 'blue', 'black'
基本思想: 在子类型构造函数内部调用超类型构造函数
function SuperType() { this.colors = ['red', 'blue'] } function SubType() { // 继承了SuperType SuperType.call(this) } var instance1 = new SubType() instance1.push('black') console.log(instance1.colors) //'red', 'blue', 'black' var instance2 = new SubType() console.log(instance2.colors) //'red', 'blue'
经过使用call()
方法或者apply()
方法,咱们其实是在(未来)新建立的SubType实例
的环境下调用SuperType构造函数
。
传递参数:
function SuperType(name) { this.name = name } function SubType() { // 继承了SuperType,同时还传递了参数 SuperType.call(this, 'zhangsan') this.age = 20 } var instance = new SubType() console.log(instance.name) // zhangsan console.log(instance.age) // 20
缺点:和构造函数模式存在同样的问题,函数没法复用。
原型链和借用构造函数的技术结合到一块,发挥二者之长的继承模式
基本思想:使用原型链
实现对原型属性和方法的继承,经过借用构造函数
来实现对实例属性的继承。这样既经过在原型上定义方法实现了函数的复用,又可以保证每一个函数都有本身的属性。
function SuperType(name) { this.name = name this.color = ['red', 'blue'] } SuperType.prototype.getName = function() { console.log(this.name) } function SubType(name, age) { SuperType.call(this, name) // 继承属性 this.age = age } SubType.prototype.getAge = function() { console.log(this.age) } SubType.prototype = new SuperType() // 继承方法 SubType.prototype.constructor = SubType var instance1 = new SubType('zhangsan', 18) instance1.colors.push('black') console.log(instance1.colors) // 'red', 'blue', 'black' console.log(instance1.getName) // 'zhangsan' console.log(instance1.getAge) // 18 var instance2 = new SubType('lisi', 20) console.log(instance2.colors) // 'red', 'blue' console.log(instance2.getName) // 'lisi' console.log(instance2.getAge) // 20
Object.create()
javascript主要是经过原型链实现继承。原型链的构建是经过将一个类型的实例赋值给另外一个构造函数的原型实现的。这样,子类型可以访问超类型的全部属性和方法。原型链的问题是对象实例共享全部继承的属性和方法,所以不适合单独使用。解决这个问题的技术是借用构造函数,即在子类型构造函数的内部调用超类型构造函数。这样就能够作到每一个实例都有本身的属性,同时还能保证只是用构造函数模式来定义类型。使用最多的继承模式是组合继承,这种模式使用原型链继承共享的属性和方法,而经过借用构造函数继承实例属性。
1. class关键字 2. 定义属性:constructor(xxx) { this.xxx = xxx } 3. 定义方法,方法之间不须要“;”
class Person{ // 使用class,而不是function constructor(name, age=18) { // 类的传参 this.name = name // 定义此类的属性 this.age = age } introduce() { // 定义方法 return `我叫${this.name},今年${this.age}岁` } sayName() { console.log(this.name) } sayAge() { console.log(this.age) } } const me = new Person('张三', 20) console.log(me.introduce())
class Coder extends Person{ constructor(name, age, job="Html") { // 继承父类属性,并新加属性 super(name, age) // 必须传参;子类必须在constructor中调用super,不然报错(由于子类没有本身的this对象,而是继承父类的this对象并对其加工,若是不调用super,子类得不到this对象) this.job = job } showJob() { // 子类的新方法 console.log(this.job) } } // 调用 const coder1 = new Coder('李四', 22, 'js') coder1.sayName() coder1.showJob()