全手打原创,转载请标明出处:https://www.cnblogs.com/dreamsqin/p/13745195.html, 多谢,=。=~(如果对你有帮助的话请帮我点个赞啦)
重新学习JavaScript是因为当年转前端有点儿赶鸭子上架的意味,我一直在反思我的知识点总是很零散,不能在脑海中形成一个完整的体系,所以这次想通过再次学习将知识点都串联起来,结合日常开发的项目,达到温故而知新的效果。与此同时,总结一下我认为很重要但又被我遗漏的知识点~
new
命令调用构造函数会导致函数内部原本指向实例对象的this
指向全局对象,致使构造函数中的变量、方法成为全局变量、全局方法。// 方案一:在构造函数内部使用严格模式(严格模式中,函数内部的this不能指向全局对象)
function Fubar(foo, bar){
'use strict';
this._foo = foo;
this._bar = bar;
}
Fubar()
// TypeError: Cannot set property '_foo' of undefined
// 方案二:构造函数内部判断是否使用new命令,如果发现没有使用,则直接返回一个实例对象
function Fubar(foo, bar) {
if (!(this instanceof Fubar)) {
return new Fubar(foo, bar);
}
// 还可以通过new.target判断,如果当前函数是new命令调用,new.target指向当前函数,否则为undefined。
// if(!new.target) {
// return new Fubar(foo, bar);
// }
this._foo = foo;
this._bar = bar;
}
Fubar(1, 2)._foo // 1
(new Fubar(1, 2))._foo // 1
return
语句,且return
后面跟着一个对象,new
命令会返回return
语句指定的对象;否则,就会不管return
语句,返回this
对象。var Vehicle = function () {
this.price = 1000;
return 1000;
};
(new Vehicle()) === 1000 // false
var Vehicle = function (){
this.price = 1000;
return { price: 2000 };
};
(new Vehicle()).price
// 2000
new
命令原理使用new命令时,它后面的函数依次执行下面的步骤:
prototype
属性。this
关键字。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);
可以使用Object.setPrototypeOf()
方法模拟:
var F = function () {
this.foo = 'bar';
};
var f = new F();
// 等同于
// 将一个空对象的原型设为构造函数的prototype属性(F.prototype)
var f = Object.setPrototypeOf({}, F.prototype);
// 将构造函数内部的this绑定这个空对象,然后执行构造函数,使得定义在this上面的方法和属性(this.foo),都转移到这个空对象上。
F.call(f);
function Animal(name) {
this.name = name;
}
Animal.prototype.color = 'white';
var cat1 = new Animal('大毛');
var cat2 = new Animal('二毛');
cat1.color // 'white'
cat2.color // 'white'
Animal.prototype.color = 'yellow';
cat1.color // "yellow"
cat2.color // "yellow"
prototype
属性会自动成为实例对象的原型,对象到原型,再到原型的原型...形成原型链,原型链的尽头是null
。Object.getPrototypeOf(Object.prototype)
// null
prototype
对象有一个constructor
属性,默认指向prototype
对象所在的构造函数,可以被所有实例对象继承。function P() {}
var p = new P();
p.constructor === P // true
p.constructor === P.prototype.constructor // true
p.hasOwnProperty('constructor') // false
constructor
属性从一个实例对象新建另一个实例。function Constr() {}
var x = new Constr();
var y = new x.constructor();
y instanceof Constr // true
constructor
属性是什么函数,可以通过name
属性从实例得到构造函数的名称。function Foo() {}
var f = new Foo();
f.constructor.name // "Foo"
constructer
属性的指向。// 坏的写法
C.prototype = {
method1: function (...) { ... },
// ...
};
// 好的写法
C.prototype = {
constructor: C,
method1: function (...) { ... },
// ...
};
// 更好的写法
C.prototype.method1 = function (...) { ... };
Object.prototype.__proto__
可以获取实例对象的原型,只有浏览器需要部署,其他环境可以没有这个属性,所以尽量不用,应使用Object.PrototypeOf()
和Object.setPrototypeOf()
进行读写操作。var obj = {};
var p = {};
obj.__proto__ = p;
Object.getPrototypeOf(obj) === p // true
instanceof
运算符instanceof
返回一个布尔值,表示对象是否为某个构造函数的实例。var d = new Date();
d instanceof Date // true
d instanceof Object // true
// 等同于
Date.prototype.isPrototypeOf(d) // true
null
)都是Object
的实例,所以instanceof
运算符可以判断一个值是否为非null
的对象。var obj = { foo: 123 };
obj instanceof Object // true
null instanceof Object // false
undefined instanceof Object // false
instanceof
运算符只能用于对象,不适用原始类型的值。var s = 'hello';
s instanceof String // false
// 第一步:在子类的构造函数中,调用父类的构造函数(在实例上调用父类的构造函数Super,就会让子类实例具有父类实例的属性)
function Sub(value) {
Super.call(this);
this.prop = value;
}
// 第二步:让子类的原型指向父类的原型(这样子类就可以继承父类原型)
// 不是直接等于Super.prototype,否则后面对Sub.prototype的操作,会连父类的原型Super.prototype一起修改掉
Sub.prototype = Object.create(Super.prototype);
Sub.prototype.constructor = Sub;
Sub.prototype.method = '...';
ClassB.prototype.print = function() {
ClassA.prototype.print.call(this);
// some code
}
JavaScript 不提供多重继承功能,即不允许一个对象同时继承多个对象。但是,可以通过变通方法,实现这个功能。
// 子类S同时继承了父类M1和M2,这种模式又称为 Mixin(混入)。
function M1() {
this.hello = 'hello';
}
function M2() {
this.world = 'world';
}
function S() {
M1.call(this);
M2.call(this);
}
// 继承 M1
S.prototype = Object.create(M1.prototype);
// 继承链上加入 M2
Object.assign(S.prototype, M2.prototype);
// 指定构造函数
S.prototype.constructor = S;
var s = new S();
s.hello // 'hello'
s.world // 'world'
如果要拷贝一个对象,需要做到下面两件事情。
function copyObject(orig) {
var copy = Object.create(Object.getPrototypeOf(orig));
copyOwnPropertiesFrom(copy, orig);
return copy;
}
function copyOwnPropertiesFrom(target, source) {
Object
.getOwnPropertyNames(source)
.forEach(function (propKey) {
var desc = Object.getOwnPropertyDescriptor(source, propKey);
Object.defineProperty(target, propKey, desc);
});
return target;
}
// ES2017引入Object.getOwnPropertyDescriptors后的简便写法
function copyObject(orig) {
return Object.create(
Object.getPrototypeOf(orig),
Object.getOwnPropertyDescriptors(orig)
);
}
早期的 JavaScript 语言有很多设计不合理的地方,但是为了兼容以前的代码,又不能改变老的语法,只能不断添加新的语法,引导程序员使用新语法。
严格模式是从 ES5 进入标准的,主要目的有以下几个:
this
关键字指向全局对象。fn.callee
、fn.caller
。arguments.callee
、arguments.caller
。length
属性赋值:'abc'.length = 5
。configurable: false
)。Object.preventExtensions(obj);obj.v = 1;
。eval
、arguments
不可用作标识名。function f(a, a, b) {}
。var n = 0100;
。with
语句。eval
作用域。arguments
不再追踪参数的变化。function f(a) {
a = 2;
return [a, arguments[0]];
}
f(1); // 正常模式为[2, 2]
function f(a) {
'use strict';
a = 2;
return [a, arguments[0]];
}
f(1); // 严格模式为[2, 1]
implements
、interface
、let
、package
、private
、protected
、public
、static
、yield
。在单个脚本的开头或者某个函数的开头使用字符串'use strict'
。
<script>
'use strict';
console.log('这是严格模式');
</script>
function strict() {
'use strict';
return '这是严格模式';
}
// 不同模式的脚本合并时应把整个脚本文件放在一个立即执行的匿名函数之中,防止出错。
(function () {
'use strict';
// some code here
})();
JavaScript 语言入门教程 :https://wangdoc.com/javascript/index.html