---- 本系列仅为个人学习总结,基于阮一峰大佬的《>>JavaScript 教程<<》---
目录
补充:
注意点
五.严格模式
典型的面向对象编程语言(比如 C++ 和 Java),都有“类”(class)这个概念。所谓“类”就是对象的模板,对象就是“类”的实例。但是,JavaScript 语言的对象体系,不是基于“类”的,而是基于构造函数(constructor)和原型链(prototype)。
JavaScript 语言使用构造函数(constructor)作为对象的模板。所谓”构造函数”,就是专门用来生成实例对象的函数。它就是对象的模板,描述实例对象的基本结构。一个构造函数,可以生成多个实例对象,这些实例对象都有相同的结构。
构造函数的特点:
- 函数体内部使用了
this
关键字,代表了所要生成的对象实例- 生成对象的时候,必须使用
new
命令
一.new 命令
作用: 执行构造函数,返回一个实例对象
var Vehicle = function () {
this.price = 1000;
};
var v = new Vehicle();
v.price // 1000
new命令执行时,构造函数内部的this,就代表了新生成的实例对象
new
命令本身就可以执行构造函数,所以后面的构造函数可以带括号,也可以不带括号, 但是为了表示这里是函数调用,推荐使用括号。
若不使用new
命令,直接调用构造函数会怎样?
此时
- 构造函数变成了普通函数,并不会生成实例对象
-
this
代表了全局对象
var Vehicle = function (){
this.price = 1000;
};
var v = Vehicle();
v // undefined
price // 1000 ,(全局变量)
原本this在最外层代码中便是全局对象, 即
补充:
博客园 / ~_~ / >>全局对象和this指针<<
以及阮一峰老师的文章 >>JavaScript 的 this 原理<<
- 为了保证构造函数必须与
new
命令一起使用,一个解决办法是,构造函数内部使用严格模式,即第一行加上use strict
。这样的话,一旦忘了使用new
命令,直接调用构造函数就会报错。
function Animal(age){
'use strict';
this._age = age;
};
var cat = Animal();
//Uncaught TypeError: Cannot set property '_age' of undefined
at Animal
var dog = new Animal(22);
dog._age //22
- 另一个解决办法,构造函数内部判断是否使用
new
命令,如果发现没有使用,则直接返回一个实例对象。
function Animal(age){
if(!(this instanceof Animal)){
return new Animal(age);
}
this._age=age;
};
var cat = Animal(11);
cat._age;//11
var dog = new Animal(22);
dog._age //22
new
命令简化的内部流程,
可以用下面的代码表示
function _new(/* 构造函数 */ constructor, /* 构造函数参数 */ params) {
// 将 arguments 对象转为数组
var args = [].slice.call(arguments);
// 取出构造函数
var constructor = args.shift();
// 创建一个空对象,继承构造函数的 prototype 属性
var context = Object.create(constructor.prototype);
// 执行构造函数
var result = constructor.apply(context, args);
// 如果返回结果是对象,就直接返回,否则返回 context 对象
return (typeof result === 'object' && result != null) ? result : context;
}
// 实例
var actor = _new(Person, '张三', 28);
new.target属性
函数内部可以使用new.target属性。如果当前函数是new
命令调用,new.target
指向当前函数,否则为undefined,
使用这个属性,可以判断函数调用的时候,是否使用new
命令。
function f() {
if (!new.target) {
throw new Error('请使用 new 命令调用!');
}
// ...
}
f() // Uncaught Error: 请使用 new 命令调用!
Object.creat()方法
构造函数作为模板,可以生成实例对象。但是,有时拿不到构造函数,只能拿到一个现有的对象。我们希望以这个现有的对象作为模板,生成新的实例对象,这时就可以使用Object.creat()方法。
var catA = {
name : 'mimi',
age : 2,
talk: function(){
console.log('Miao~,my name is '+this.name+'\n');
}
};
var catB = Object.create(catA);
catB.talk(); // Miao~,my name is mimi
catB.age //2
对象catA
是catB
的模板,后者继承了前者的属性和方法
二.this 关键字
设计目的:在函数体内部,指代函数当前的运行环境
var f = function () {
console.log(this.x);
}
var x = 1;
var obj = {
f: f,
x: 2,
};
// 单独执行
f() // 1
// obj 环境执行
obj.f() // 2
JavaScript 支持运行环境动态切换,也就是说,this
的指向是动态的,没有办法事先确定到底指向哪个对象
- 由于对象的属性可以赋给另一个对象,所以属性所在的当前对象是可变的,即
this
的指向是可变的。
var A = {
name: '张三',
describe: function () {
return '姓名:'+ this.name;
}
};
var B = {
name: '李四'
};
B.describe = A.describe;
B.describe()
// "姓名:李四"
如果this
所在的方法不在对象的第一层,这时this
只是指向当前一层的对象,而不会继承更上面的层。
var a = {
p: 'Hello',
b: {
m: function() {
console.log(this.p);
}
}
};
a.b.m() // undefined
上面代码中,a.b.m
方法在a
对象的第二层,该方法内部的this
不是指向a
,而是指向a.b
,因为实际执行的是下面的代码。
var b = {
m: function() {
console.log(this.p);
}
};
var a = {
p: 'Hello',
b: b
};
(a.b).m() // 等同于 b.m()
如果要达到预期效果,只有写成下面这样。
var a = {
b: {
m: function() {
console.log(this.p);
},
p: 'Hello'
}
};
如果这时将嵌套对象内部的方法赋值给一个变量,this
依然会指向全局对象。
var a = {
b: {
m: function() {
console.log(this.p);
},
p: 'Hello'
}
};
var hello = a.b.m;
hello() // undefined
上面代码中,m
是多层对象内部的一个方法。为求简便,将其赋值给hello
变量,结果调用时,this
指向了顶层对象。为了避免这个问题,可以只将m
所在的对象赋值给hello
,这样调用时,this
的指向就不会变。
var hello = a.b;
hello.m() // Hello
>> this本质 <<
注意点
Ⅰ.避免多层 this
var o = {
f1: function () {
console.log(this);
var f2 = function () {
console.log(this);
}();
}
}
o.f1()
// Object
// Window
★实际执行的是下面的代码
var temp = function () {
console.log(this);
};
var o = {
f1: function () {
console.log(this);
var f2 = temp();
}
}
①一个常见的解决方法是在第二层改用一个指向外层this
的变量
var o = {
f1: function() {
console.log(this);
var that = this;
var f2 = function() {
console.log(that);
}();
}
}
o.f1()
// Object
// Object
②JavaScript 提供了严格模式,也可以硬性避免这种问题。严格模式下,如果函数内部的this
指向顶层对象,就会报错。
var counter = {
count: 0
};
counter.inc = function () {
'use strict';
this.count++
};
var f = counter.inc;
f()
// TypeError: Cannot read property 'count' of undefined
上面代码中,inc
方法通过'use strict'
声明采用严格模式,这时内部的this
一旦指向顶层对象,就会报错。
Ⅱ.避免数组处理方法中的 this
var o = {
v: 'hello',
p:['玛卡巴卡','唔西迪西'],
f: function f() {
this.p.forEach(function (item) {
console.log(this.v + ' ' + item);
});
}
}
o.f()
>>undefined a1
>>undefined a2
解决方法👇👇👇
-----------------------
法一:使用中间变量固定this
var o={
v:'hello',
p:['玛卡巴卡','唔西迪西'],
f:function f(){
var that=this;
this.p.forEach(function(item){console.log(that.v+' '+item)});
}
}
o.f()
>>hello 玛卡巴卡
>>hello 唔西迪西
------------------------
法二:将this当作foreach方法的第二个参数,固定它的运行环境
var o = {
v: 'hello',
p:['玛卡巴卡','唔西迪西'],
f: function f() {
this.p.forEach(function (item) {
console.log(this.v + ' ' + item);
}, this);
}
}
o.f()
>>hello 玛卡巴卡
>>hello 唔西迪西
//Ⅲ.避免回调函数中的 this
>> ※绑定 this 的方法 <<
(使用call
、apply
、bind
这三个方法,来切换/固定this
的指向。)
三.对象的继承
四.Object 对象的相关方法
五.严格模式