什么是构造函数
构造函数就是一个普通的函数,里面可以写任何语句(逻辑语句或DOM操作) ,可以new出新的实例,使其实例可以共享构造函数的原型
第一个例子
function Fun() {
this.name = "小明";
this.age = 12;
this.sex = "男";
console.log("aa")//"aa""
}
var obj = new fun();
console.log(obj.age); //12
用new运算符调用一个函数的时候,会经历四步走(简洁版本)
- 创建新对象 ;
- 给新对象的内部属性赋值,关键是给
[[Prototype]]
属性赋值(默认为字符串‘proto’),构造原型链(如果构造函数的原型是 Object 类型,则指向构造函数的原型;不然指向 Object 对象的原型); - 执行构造函数 ,执行过程中内部
this
指向新创建的对象o
; - 如果 构造 内部显式返回对象类型数据,返回该数据,执行结束;不然返回新创建的对象
注意第四步:当构造函数自己有return的时候:当构造函数中自身的retuen的是数值那么只是会结束函数执行,但是返回值还是那个定义的{},如果自带的return是一个函数,那么返回的是那个自带的函数
1.一般我们可以用new来多次调用一个函数,总可以得到具有相同属性群的对象,但不相同,
if (obj === obj2) {
console.log("相等");
} else {
console.log("不相等");
}
这里返回不相等因为虽然他们都是调用的同一个函数中生产的对象但他们的堆地点是不同的(引用类型也就是对象他们的值相同不代表他们就相同他们的位置还不一样);
图片理解
2.new就是一个运算符并不是c c++ java中的类的概念
3.构造函数就是一个普通的函数,里面可以写任何语句(逻辑语句或DOM操作)
if (this.age < 18) {
console.log("未成年");
} else if (this.age > 18) {
console.log("成年");
} else {
console.log("请输入年龄");
}
上文就用到了if语句 来判断age对象中的age属性的值;
4.上面讲到new四步走中:所有语句执行后,函数retum这个对象;(上面的例子将函数返回给力obj他现在是个对象了);但当函数中写了return会怎么样?
如果ruturn 的是一个基本类型那么忽视这个return,但return一样会会阻止构造函数return后面语句的执行
this.name = "小明";
this.age = age;
if (this.age < 18) {
console.log("未成年");
} else if (this.age > 18) {
console.log("成年");
} else {
console.log("请输入年龄");
}
return 3; //这里的3不会返回,但sex属性也没用了,aa也不会输出因为被return阻止了
this.sex = "男";
console.log("aa")//"aa""
如果ruturn 的是一个引用类型那么原有的return对象会被覆盖
function fun2(name, age) {
this.name = name;
return { "a": 1, "b": 2 };
this.age = age;
}
var xiaoming = new fun2("小明", 12);
console.log(xiaoming); //{a: 1, b: 2} 原来的return对象就被覆盖了
这里就有把return设置为{ } (构架函数的工厂模式四步走)
function People(name, age) {
var obj = {};
obj.name = name;
obj.age = age;
return obj;
}
var xiaoming = new People("小明", 12);
console.log(xiaoming);
注意
1.当我们设置prototype为非对象类型
如果构造函数的原型不满足形成原型链的要求,那就跳过直接和内建对象原型关联
重写构造函数原型属性为非对象类型,实例内部 Prototype 属性指向 Object 原型对象
因为实例是一个对象类型的数据,默认会继承内建对象的原型,
function Foo(name) {
this.name = name;
}
var o1 = new Foo("xiaoming");
console.log(o1.__proto__ === Foo.prototype); // true
Foo.prototype = 1;
var o2 = new Foo("xiaohong");
console.log(o2.__proto__ === Foo.prototype); // false
console.log(o2.__proto__ === Object.prototype); // true
2. 构造函数执行返回结果是对象的情况
手写new
- 创建新对象
- 给新对象的内部属性赋值,关键是给
[[Prototype]]
属性赋值(默认为字符串‘proto’),构造原型链(如果构造函数的原型是 Object 类型,则指向构造函数的原型;不然指向 Object 对象的原型);// 这一条是创建新Object的规则 - 执行构造函数,将其构造函数中的this设置为第一步创建中的新对象
- 判断构造函数的返回值,如果返回值是对象类型(instanceof Object === true)就返回函数返回值,否则返回创建的新函数
function createNew() {
// 获取构造函数
const Fun = Array.prototype.shift.call(arguments)
// 1
const obj = new Object()
// 2
Object.setPrototypeOf(obj, Fun.prototype)
// 3
let res = Fun.apply(obj, arguments)
// 4
return res instanceof Object ? res : obj
}
优化
警告: 由于现代 JavaScript 引擎优化属性访问所带来的特性的关系,更改对象的
[[Prototype]]
在各个浏览器和 JavaScript 引擎上都是一个很慢的操作。其在更改继承的性能上的影响是微妙而又广泛的,这不仅仅限于obj.__proto__ = ...
语句上的时间花费,而且可能会延伸到任何代码,那些可以访问任何[[Prototype]]
已被更改的对象的代码。如果你关心性能,你应该避免设置一个对象的[[Prototype]]
。相反,你应该使用Object.create()
来创建带有你想要的[[Prototype]]
的新对象。
const isObject = function (obj) {
// return obj.toString() = "[object objrect]"
return obj instanceof Object
}
let myNew = function (fn, ...args) {
// 第一条和第2条合并
let obj = isObject(fn.prototype) ? Object.create(fn.prototype) : Object.create(Object.prototype)
let res = fn.apply(obj, args)
return isObject(res) ? res : obj
}