构造函数


new 关键字来调用的函数,称为构造函数。当一个函数执行 new 操作时,会执行以下三步

  • 1、生成空对象
  • 2、将this指向创建的空对象
  • 3、返回空对象

new的作用就是创建或返回了一个对象

// 通俗来说,就是执行了类似如下这样的事
function Person(name){
  var this  = {};   // 创建this变量,指向空对象
  this.name = name; // 属性和方法被加入到this引用的对象中
  this.say  = function () { return "I am " + this.name; }
  return this;      // 返回this对象
}

-- 如果 return 的是一个对象,则返回该对象,而不是 this。如果返回的是原始类型,则依旧返回 this

// 返回
function User1(name) { this.name = name; return { id: 1 };}
new User1("Onion"); // {id: 1}

function User2(name) { this.name = name; return "user"; }
new User2("Onion"); // {name: 'Onion'}

-- 构造函数内定义的函数和原型链上的函数。【构造函数】为每次 new 操作新建一个 function 对象,而【原型链上的函数】指向同一个 function 对象

function User(name) { this.name = name; this.sayHi = function() { }; }

User.prototype.sayHiAgain = function() {
    console.log(`My name is ${this.name}`);
};
const onion  = new User("Onion");
const garlic = new User("Garlic");

console.log(onion.sayHi === garlic.sayHi); // false
console.log(onion.sayHiAgain === garlic.sayHiAgain); // true

JS对象分为函数对象与普通对象


  • JS中的内置对象,如StringNumberBooleanRegExpDateArrayObjectFunctionError等,因为他们是native代码实现的,他们的原型打印出来都是ƒ () { [native code] }内置对象本质上也是函数,所以可以通过他们创建对象,创建出的对象的隐式原型(__proto__)指向对应内置对象的prototype属性,最顶层的原型对象依然指向Object.prototype
'abc'.__proto__ === String.prototype;   // true 
new String('abc').__proto__ === String.prototype;  //true

new Number(1).__proto__  ==== Number.prototype;   // true

[1,2,3].__proto__ === Array.prototype;            // true
new Array(1,2,3).__proto__ === Array.prototype;   // true

({}).__proto__ === Object.prototype;               // true 
new Object({}).__proto__ === Object.prototype;     // true

var f = function() {};
f.__proto__ === Function.prototype;            // true
var f = new Function('{}');
f.__proto__ === Function.prototype;            // true

函数对象,其实就是 JavaScript 的用函数来模拟的类实现。 所有 Function 的实例都是函数对象,其他的均为普通对象,其中包括 Function 实例的实例。

Function.__proto__ === Function.prototype//true
function fun1(){};
const fun2 = function(){};
const fun3 = new Function('name','console.log(name)');

const obj1 = {};
const obj2 = new Object();
const obj3 = new fun1();
const obj4 = new new Function();
console.log(typeof Object);   // function
console.log(typeof Function); // function
console.log(typeof fun1);     // function
console.log(typeof fun2);     // function
console.log(typeof fun3);     // function

console.log(typeof obj1);     // object
console.log(typeof obj2);     // object
console.log(typeof obj3);     // object
console.log(typeof obj4);     // object

// 每个对象都有__proto__属性,但是只有函数对象才有prototype属性
fun1.prototype // {constructor: ƒ}
obj1.prototype // undefined

// **准则1:原型对象(即Person.prototype)的constructor指向构造函数本身**
Person.prototype.constructor == Person
// **准则2:实例(即person01)的__proto__和原型对象指向同一个地方**
person01.__proto__ == Person.prototype

new Animal()

Dog.prototype === animal.proto === Animal.prototype

function Animal(){  
    this.type = "animal";  
}
Animal.prototype.getType = function(){  
    return this.type;  
}
function Dog(){  
    this.name = "dog";
}
Dog.prototype = new Animal();

var xiaohuang = new Dog();



//原型链关系  
xiaohuang.__proto__ === Dog.prototype
Dog.prototype.__proto__ === Animal.prototype
Animal.prototype.__proto__ === Object.prototype
Object.prototype.__proto__ === null

instanceof和typeof


instanceof 运算符用于测试构造函数的 prototype 属性是否出现在对象原型链中的任何位置

原型和原型链


proto(也就是[[Prototype]]

<1>constructor,prototype和__proto的关系

<2>Function instanceof Object 和Object instanceof Function的结果为什么都是true

<3>为什么所有的对象的原型最终都指向Object.prototype而不是Object

作用域


闭包


this


JavaScript 中 this 的四条绑定规则

https://fecoding.cn/2016/09/15/the-rule-of-binding-this-in-javascript/

二、JavaScript中this的四种情况(this是函数的内置对象,所以,只能出现在函数内部)

this所在函数fn无人调用的情况下this指向window

默认绑定,在不能应用其它绑定规则时使用的默认规则

默认绑定

隐式绑定 xxx.fn() 函数的调用是在某个对象上触发的,即调用位置上存在上下文对象。典型的形式为 XXX.fun() XXX.fn();fn()前如果什么都没有,那么肯定不是隐式绑定

一个对象中一个带有this的方法被调用后,this指向window,不是调用它的对象

显示绑定 xxx.call()

箭头函数 () => {}

person1.show4()() 相当于 var func = person3.show();func()

var age = 18; var obj = { age : 21, fn: functoin(){ console.log(this.age) } } (false || obj.fn)() // 18复制代码在上面的代码中,在对象中的fn方法执行之前,会先执行‘||’运算,相当于: (false || function(){ console.log(this.age) })()

setTimeout 中的回调直接指向window

call()函数


基础类型相加

字符串的优先级高,当遇到一边是字符串时,都转换为字符串再拼接。不是字符串情况下,尝试把两边的操作变量转换为数字,如果两边都转换成功,则进行数字加法。

注:这里转换为数字调用的是`Number`,字符串调用的是`toString`,有括号时括号优先
```js 1 + 1 = 2 1 + '2' = '12' 1 + false = 1 1 + null = 1 1 + undefined = NaN false + false = 0 false + null = 0 false + undefined = NaN null + undefined = NaN (1 + 1) + '1' = '21' NaN + 1 = NaN NaN + '1' = NaN1 ```

引用对象类型

Object 对象在进行 "+" 操作时,会先调用自己的 toString 方法,再进行字符串拼接

[] + []           = ''
[] + [[1]]        = '1'
[] + [{ a: 1}]    = '[object object]' // {a: 1}调用toString()先转为[object object]
[] + ({})         = '[object object]'
({}) + [1]        = '[object object]1'
({}) + ({})       = '[object object][object object]'
({}) + ({a: [1]}) = '[object object][object object]'
+{}               = 'NaN' // 由于控制台解析导致,请看以下解释

注:console.log({}+{})得到的这样的结果,但是,在有些浏览器例如Firefox、Edge控制台直接输入{}+{},会得到NaN,因为浏览器会把{} + {}直译为相当于+{}语句,因为它们会认为以花括号开头({)的,是一个区块语句的开头,而不是一个对象字面量,所以会认为略过第一个{},把整个语句认为是个+{}的语句,也就是相当于强制求出数字值的Number({})函数调用运算,相当于Number("[object Object]")运算,最后得出的是NaN。如果加上圆括号({}) + {},则可以避免这样的问题。

基础类型 + 引用对象类型

基础类型和引用对象类型进行 “+” 号运算时,两遍都转换为字符串后再进行比较。

1 + []
1 + ({})         = '1[object object]'
'2' + []         = '2'
'2' + ({})       = '2[object object]'
false + []       = 'false'
false + [{}]     = 'false[object object]'
false + ({})     = 'false[object object]'
null + []        = 'null'
null + ({})      = 'null[object object]'
null + [{}]      = 'null[object object]'
undefined + []   = 'undefined'
undefined + ({}) = 'undefined[object object]'
undefined + [{}] = 'undefined[object object]'
NaN + []         = 'NaN'

// 复杂例子
[1,2,['abc'],[4,5,{ name:'abe' }]].toString() = '1,2,abc,4,5,[object object]' 

函数对象toString结果

Function进行“+”号运算时也遵循先转换为字符串后再运算。
FunctiontoString 方法不是返回 [object Object],而是将整个方法体返回为字符串,连换行和缩进都完整保持。 FunctiontoString,返回的是定义函数时,等号右边的全部内容字符串。func3等价于func1。

let func1 = function(){} // func1.toString() 结果为 'function(){}'
let func2 = (a, b) => {} // func2.toString() 结果为 '(a, b) => {}'
function func3(){}       // func3.toString() 结果为 'function func3(){}'

1 + func1                // '1function(){}'
({}) + func1             // '[object Object]function(){}'
false + func1            // 'falsefunction(){}'
1 + func2                // '1(a, b) => {}'

总结

  • 都是基础类型,存在字符串时,两边转为字符串再运算,不存在字符串,两边转为数字再运算
  • 存在引用对象,两边转换为字符串后再运算