new 运算符建立一个用户定义的对象类型的实例或具备构造函数的内置对象的实例javascript
this
的上下文 ;this
。
- 当构造函数没有返回值时:
function Animal (name, age) {
this.name = name;
this.age = age;
this.speak = '汪汪'
}
Animal.prototype.color = 'red';
Animal.prototype.say = function () {
console.log('我会叫--' + this.speak);
}
var dog = new Animal('旺财', '3');
console.log(dog.name) // 旺财
// 打印一个没有的属性
console.log(dog.eat) // undefined
console.log(dog.age) // 3
console.log(dog.speak) // 汪汪
console.log(dog.color) // red
dog.say(); // 我会叫--汪汪
复制代码
- 当构造函数有返回值,且返回值类型为对象的(不包含 null )
有返回对象的,new 实例化后的对象就是返回的对象。拿不到内部其余的属性,也不会获取到原型上的属性和方法java
function Animal2 (name, age) {
this.name = name;
this.age = age;
this.speak = '汪汪'
return {
name,
eat: '吃骨头',
}
}
Animal2.prototype.color = 'red';
Animal2.prototype.say = function () {
console.log('我会叫--' + this.speak);
}
var dog = new Animal2('旺财', '3');
console.log(dog.name) // 旺财
console.log(dog.eat) // 吃骨头
console.log(dog.age) // undefined
console.log(dog.speak) // undefined
console.log(dog.color) // undefined
dog.say(); // 报错: dog.say is not a function
复制代码
- 构造函数返回 null,
这种状况实际上和第一种状况同样(至关于构造函数没有返回值)git
当时确实要注意的是,typeof null === 'object' 全部在实现的时候,咱们要进行处理。不能看到 typeof 返回的是object,那么就返回该值。具体实例看下面的讲解。es6
function Animal3 (name, age) {
this.name = name;
this.age = age;
this.speak = '汪汪'
return null
}
Animal3.prototype.color = 'red';
Animal3.prototype.say = function () {
console.log('我会叫--' + this.speak);
}
var dog = new Animal3('旺财', '3');
console.log(dog.name) // 旺财
console.log(dog.age) // 3
console.log(dog.speak) // 汪汪
console.log(dog.color) // red
dog.say(); // 我会叫--汪汪
复制代码
function fakeNew() {
var obj = {};
// 获取第一个参数,而且会删除第一个参数
var Constructor = [].shift.call(arguments);
// obj 继承构造函数上的方法
obj.__proto__ = Constructor.prototype;
// Constructor 方法中的 this 指向 obj, 执行 Constructor 方法,至关于给 obj 继承了Constructor 中的属性,方法。 (能够理解就是经过 apply 实现继承)
var result = Constructor.apply(obj, arguments);
if(typeof result === 'object'){
// result || obj 防止返回的是 null(由于 typeof null == 'object');
return result || obj;
} else {
return obj;
}
};
复制代码
咱们来简写一下 New ,经过 ES6 写很简单 3 行代码搞定。github
function fakeNew(Constructor, ...rest) {
// Object.create 方法建立一个新对象,使用现有的对象来提供新建立的对象的__proto__。
var obj = Object.create(Constructor.prototype),
var result = Constructor.apply(obj, rest);
return typeof result === 'object' ? result || obj : obj;
};
复制代码
为何会经过 Object.create 方法来实现让对象的原型继承 Constructor.prototype 属性呢?数组
因为现代 JavaScript 引擎优化属性访问所带来的特性的关系,更改对象的
[[Prototype]]
在***各个***浏览器和 JavaScript 引擎上都是一个很慢的操做。其在更改继承的性能上的影响是微妙而又普遍的,这不单单限于obj.__proto__ = ...
语句上的时间花费,并且可能会延伸到***任何***代码,那些能够访问***任何***[[Prototype]]
已被更改的对象的代码。若是你关心性能,你应该避免设置一个对象的[[Prototype]]
。相反,你应该使用Object.create()
来建立带有你想要的[[Prototype]]
的新对象。浏览器
出自 MDN 原话app
也就是说 Object.setPrototypeOf(obj, Constructor.prototype)
和wordpress
var obj = {};obj.__proto__ = Constructor.prototype
两种方法,不管在写法仍是性能上都没有 Object.create
方法来的更好函数
上面模拟实现的 new 方法中
var result = Constructor.apply(obj, rest)
; 这行代码作了什么?
其实就是经过 apply 来实现属性和方法的继承。不清楚的同窗能够看下 apply 的模拟实现过程。
Function.prototype.fakeApply = function (obj, arr) {
var context = obj || window;
context.fn = this;
var result;
if (!arr) {
result = context.fn();
} else {
var args = [];
for (var i = 0, len = arr.length; i < len; i++) {
args.push('arr[' + i + ']');
}
result = eval('context.fn(' + args + ')')
}
delete context.fn
return result;
}
复制代码
咱们用 ES6 写法来简写一下 apply
Function.prototype.fakeApply = function(obj, arr = []) {
var context = obj || window;
context.fn = this;
result = context.fn(...arr);
delete context.fn;
return result
};
复制代码
同理 call 的模拟方法
// 经过...arr 接收后面全部的参数
Function.prototype.fakeCall = function(obj, ...arr) {
var context = obj || window;
context.fn = this;
// 将接受的参数展开执行
result = context.fn(...arr);
delete context.fn;
return result
};
复制代码
在开发过程当中根据实际状况选择使用方式,那么均可以用的话建议选择 call 方法,由于 call 的性能高于 aplly。
由于 apply 第二个参数是数组,最终执行的时候仍是要将数组转变成一个一个参数,来执行函数,所以性能比 apply 差 ( 也就是差一点点 )
Array.prototype.myMap = function(fn){
const arr = this;
const newArr = [];
for(let i = 0; i<arr.length; i++){
var temp = fn(arr[i], i, arr)
newArr.push(temp);
}
return newArr;
}
复制代码
完整的 map 他是有第二个参数的
Array.map( callback [, thisArg ] )
callback
原数组中的元素调用该方法后返回一个新数组。它接收三个参数,分别为 currentValue、index、array。
currentValue
callback的第一个参数,数组中当前被传递的元素。
index
callback的第二个参数,数组中当前被传递的元素的索引。
array
callback的第三个参数,调用map()方法的数组,即原数组。
thisArg
执行callback函数时this指向的对象
Array.prototype.map polyfill源码实现:地址传送门
// 实现 ECMA-262, Edition 5, 15.4.4.18
// 参考: http://es5.github.io/#x15.4.4.18
if (!Array.prototype.map) {
Array.prototype.map = function(callback, thisArg) {
var T, A, k;
if (this == null) {
throw new TypeError(" this is null or not defined");
}
// 1. 将O赋值为调用map方法的数组.
var O = Object(this);
// 2.将len赋值为数组O的长度.
var len = O.length >>> 0;
// 3.若是callback不是函数,则抛出TypeError异常.
if (Object.prototype.toString.call(callback) != "[object Function]") {
throw new TypeError(callback + " is not a function");
}
// 4. 若是参数thisArg有值,则将T赋值为thisArg;不然T为undefined.
if (thisArg) {
T = thisArg;
}
// 5. 建立新数组A,长度为原数组O长度len
A = new Array(len);
// 6. 将k赋值为0
k = 0;
// 7. 当 k < len 时,执行循环.
while(k < len) {
var kValue, mappedValue;
//遍历O,k为原数组索引
if (k in O) {
//kValue为索引k对应的值.
kValue = O[ k ];
// 执行callback,this指向T,参数有三个.分别是kValue:值,k:索引,O:原数组.
mappedValue = callback.call(T, kValue, k, O);
// 返回值添加到新数组A中.
A[ k ] = mappedValue;
}
// k自增1
k++;
}
// 8. 返回新数组A
return A;
};
}
复制代码
简易实现
Array.prototype.myReduce = function(callback, initialValue ) {
var previous = initialValue, k = 0, length = this.length;
// 若是没有传入 initialValue 就默认将数组的第 0 个赋给 previous
if (typeof initialValue === "undefined") {
previous = this[0];
k = 1;
}
if (typeof callback === "function") {
for (k; k < length; k++) {
this.hasOwnProperty(k) && (previous = callback(previous, this[k], k, this));
}
}
return previous;
};
复制代码
简易实现
Array.prototype.MyForEach = function(fn) {
const arr = this
for(let i = 0; i<arr.length; i++){
fn(arr[i],i,arr)
}
}
复制代码
const deepCopy = function(obj){
return Object.keys(obj).reduce(function (copy, item){
// 若是对象的 value 类型为 object 就再次执行 deepCopy 来实现深拷贝
copy[item] = typeof obj[item] === 'object' ? deepCopy(obj[item]) : obj[item];
return copy;
// 判断 obj 是 数组类型 仍是对象类型 这样 copy 就是 [] 或者是 {}
}, Object.prototype.toString.call(obj) === '[object Array]' ? [] : {})
}
复制代码
使用箭头函数和 ,逗号运算符,简化
const deepCopy = obj => Object.keys(obj).reduce(
(copy, item) => (copy[item] = typeof obj[item] === 'object' ? deepCopy(obj[item]) : obj[item], copy)
,Object.prototype.toString.call(obj) === '[object Array]' ? [] : {}
);
复制代码
for(var i=1;i<=3;i++)
{
  for(var i=1;i<3;i++)
  {
    console.log(i)
  }
}
// 1 2
for(let i=1;i<=3;i++)
{
  for(let i=1;i<3;i++)
  {
    console.log(i)
  }
}
// 1 2 1 2 1 2
复制代码
先执行一次再去作判断
var a = 10
do{
console.log(a--)
} while (a>2)
复制代码
先判断, 再执行
while(i<=10)
{
  循环体
  i++
}
复制代码
var obj = {
a: 1,
b: [],
c: function () {}
};
for (var key in obj) {
console.log(key);
}
// 结果是:
// a
// b
// c
复制代码
Object.prototype.objCustom = function() {};
Array.prototype.arrCustom = function() {};
var arr = [3, 5, 7];
arr.foo = 'hello';
for (var i in arr) {
console.log(i);
}
// 结果是:
// 0
// 1
// 2
// foo
// arrCustom
// objCustom
复制代码
自定义的 Array.prototype 上的方法也会遍历出来,经过原型链最后还能遍历到 arr.proto.proto Object.prototype 上的objCustom
hasOwnProperty() 方法会返回一个布尔值,指示对象自身属性中是否具备指定的属性
Object.prototype.objCustom = function() {};
Array.prototype.arrCustom = function() {};
var arr = [3, 5, 7];
arr.foo = 'hello';
for (var i in arr) {
if (arr.hasOwnProperty(i)) {
console.log(i);
}
}
// 结果是:
// 0
// 1
// 2
// foo
复制代码
数组上面自己的属性仍是会遍历出来
若是不想要自己的属性能够经过 forEach 来实现
Object.prototype.objCustom = function() {};
Array.prototype.arrCustom = function() {};
var arr = [3, 5, 7];
arr.foo = 'hello';
arr.forEach((item, index)=>{
console.log(item, index)
})
// 结果是:
// 3 0
// 5 1
// 7 2
复制代码
或者经过 for of 来实现
for of 须要遍历可迭代(iterable)的类型
var obj = {
a: 1,
b: [],
c: function () {}
};
for (var key of obj) {
console.log(key);
}
// 出错:
// Uncaught TypeError: obj is not iterable
复制代码
ES6 标准不认为 Object 对象是可迭代的,因此不能用 for-of 遍历之
可迭代的类型有 Array、 Map、Set、arguments 类对象、NodeList 这类 DOM 集合、String**、**generators
全文章,若有错误或不严谨的地方,请务必给予指正,谢谢!
参考