定义
-
proto 对象中有个属性proto,被称为隐式原型,这个隐式原型指向构造改对象的构造函数的原型,这也保证了实例能够访问在构造函数原型中定义的属性和方法。按照标准,proto 是不对外公开的,但是 chrome 和firefox的引擎却将他暴露了出来成为了一个公有属性,我们可以对其进行访问和赋值。但 ie 浏览器是不能访问这个属性的,所以不推荐大家直接操作这个属性,以免造成浏览器兼容问题。(现在
__proto__
属性被标准化了)
-
-proto- chrom 浏览器实现的对外可访问的变量 ,firefox浏览器对外实现的可访问变量<prototype>
firefox:
chrom:
- prototype 函数对象 独有的属性 重要的事情说三遍!三遍三遍
简单来说,在 javascript 中每个对象都会有一个 proto 属性,当我们访问一个对象的属性时,如果这个对象内部不存在这个属性,那么他就会去 proto 里找这个属性,这个 proto 又会有自己的 proto,于是就这样一直找下去,也就是我们平时所说的原型链的概念,从技术上讲,这种机制被称为动态调度(dynamic dispatch)或者委托。
原型对象也是普通的对象,并且也有可能有自己的原型,默认对象实际上永远不会是空的 - 它总会从Object.prototype
继承一些东西。如果要创建一个无原型的词典,我们必须显式将其原型设置为null
// 不继承任何东西。
let dict = Object.create(null);
console.log(dict.toString); // undefined
如果一个原型对象的原型不为null的话,我们就称之为原型链(prototype chain)。
原型链(Prototype chain)
定义:原型链是用于实现继承和共享属性的有限对象链。
想象一个这种情况,2个对象,大部分内容都一样,只有一小部分不一样,很明显,在一个好的设计模式中,我们会需要重用那部分相同的,而不是在每个对象中重复定义那些相同的方法或者属性。在基于类[class-based]的系统中,这些重用部分被称为类的继承 – 相同的部分放入class A,然后class B和class C从A继承,并且可以声明拥有各自的独特的东西。
ECMAScript没有类的概念。但是,重用[reuse]这个理念没什么不同(某些方面,甚至比class-更加灵活),可以由prototype chain原型链来实现。这种继承被称为delegation based inheritance-基于继承的委托,或者更通俗一些,叫做原型继承。
类似于类”A”,”B”,”C”,在ECMAScript中尼创建对象类”a”,”b”,”c”,相应地, 对象“a” 拥有对象“b”和”c”的共同部分。同时对象“b”和”c”只包含它们自己的附加属性或方法。
var a = {
x: 10,
calculate: function (z) {
return this.x + this.y + z
}
};
var b = {
y: 20,
__proto__: a
};
var c = {
y: 30,
__proto__: a
};
// 调用继承过来的方法
b.calculate(30); // 60
c.calculate(40); // 80
原理:
如果在对象b中找不到calculate方法(也就是对象b中没有这个calculate属性), 那么就会沿着原型链开始找。如果这个calculate方法在b的prototype中没有找到,那么就会沿着原型链找到a的prototype,一直遍历完整个原型链。记住,一旦找到,就返回第一个找到的属性或者方法。因此,第一个找到的属性成为继承属性。如果遍历完整个原型链,仍然没有找到,那么就会返回undefined。
刘伟硕代码:
//demo1
let cur_time = new Date().Format("yyyy-MM-dd hh:mm:ss");
Date.prototype.Format = function (fmt) {
var o = {
"M+": this.getMonth() + 1, //月份
"d+": this.getDate(), //日
"h+": this.getHours(), //小时
"m+": this.getMinutes(), //分
"s+": this.getSeconds(), //秒
"q+": Math.floor((this.getMonth() + 3) / 3), //季度
"S": this.getMilliseconds() //毫秒
};
if (/(y+)/.test(fmt)) fmt = fmt.replace(RegExp.$1, (this.getFullYear() + "").substr(4 - RegExp.$1.length));
for (let k in o)
if (new RegExp("(" + k + ")").test(fmt)) fmt = fmt.replace(RegExp.$1, (RegExp.$1.length == 1) ? (o[k]) : (("00" + o[k]).substr(("" + o[k]).length)));
return fmt;
}
注:
1.9 属性是静态的, 他不是独立的的正则表达式属性. 所以, 我们总是像这样子使用他们RegExp.$1
, ..., RegExp.$9
.
属性的值是只读的而且只有在正确匹配的情况下才会改变.
括号匹配项是无限的, 但是RegExp对象能捕获的只有九个. 你可以通过返回一个数组索引来取得所有的括号匹配项.
这些属性可以在String.replace
方法中替换字符串. 在这种情况下, 不用在前面加上RegExp。下面的例子将详细说明. 当正则表达式中不包含括号, 脚本中的 $n
's 就是字面上的意思 (当n是正整数).
2.replace() 方法用于在字符串中用一些字符替换另一些字符,或替换一个与正则表达式匹配的子串。
stringObject.replace(regexp/substr,replacement)
3.substr() 方法可在字符串中抽取从 start 下标开始的指定数目的字符。
stringObject.substr(start,length)
JS 继承
1.for in 遍历整个原型链进行继承
//demo 乱序 插进来的
function ClassA(){
this.name ="bianbumei";
this.age="88";
this.sayAge=function (){
alert(`my age is ${this.age}`)
}
}
function ClassB(){
let instanceA=new ClassA();
let me =this;
for (let oneProName in instanceA){
console.log(`${oneProName}: ${instanceA[oneProName]}`)
me[oneProName]=instanceA[oneProName]
}
console.log(me)
}
let instanceB=new ClassB();
console.log("instanceB::",instanceB)
2.使用原型链进行继承
//demo2
function ClassA() {
}
ClassA.prototype.color = "blue";
ClassA.prototype.sayColor = function () {
alert(this.color);
};
function ClassB() {
}
ClassB.prototype = new ClassA();
let instanceClassB=new ClassB()
console.log(instanceClassB)
//
console.log(`ClassB :${instanceClassB}`)
//demo3
function ClassA() {
}
ClassA.prototype.color = "blue";
ClassA.prototype.sayColor = function () {
alert(this.color);
};
function ClassB() {
}
ClassB.prototype = new ClassA();
ClassB.prototype.name = "";
ClassB.prototype.sayName = function () {
//this 指向方法的调用者
alert(this.name);
};
let instanceClassB=new ClassB()
instanceClassB.name="xiaobian";
console.log(instanceClassB)
instanceClassB.sayName();
//demo4
function ClassA() {
}
ClassA.prototype.color = "blue";
ClassA.prototype.sayColor = function () {
alert(this.color);
};
//如果将赋值语句放到ClassB的构造函数中会发生什么
function ClassB() {
ClassB.prototype = new ClassA();
}
let instanceClassB1=new ClassB()
console.log(instanceClassB1)//没有对应的方法
let instanceClassB2=new ClassB()
console.log(instanceClassB2)//有了对应的方法
//这是为什么呢?
console.log(`ClassB :${instanceClassB2}`)
继承的演变过程
1.Object.create()方法创建一个新对象,使用现有的对象来提供新创建的对象的proto。
//demos
let baba={
name:"mayun",
money:100*10000,
face:"caixukun",
car:"aotuo",
work_count_money:function(){
console.log(`${this.name}的钱,我数了!很好玩!总共有${this.money}`);
}
}
let me=Object.create(baba);
me.name="liangsijie";
me.face="shiwaxinge";
me.work_count_money();
console.log(me)
Object.create()的底层实现相当于如下代码:
function create(o){
function F(){}
F.prototype = o;
return new F();
}
2.原型链继承
- 优点:父类方法可以复用
缺点:- 父类的引用属性会被所有子类实例共享
- 子类构建实例时不能向父类传递参数
3. 构造函数继承
核心:将父类构造函数的内容复制给了子类的构造函数。这是所有继承中唯一一个不涉及到prototype的继承。
优点:和原型链继承完全反过来。
- 父类的引用属性不会被共享
- 子类构建实例时可以向父类传递参数
缺点:父类的方法不能复用,子类实例的方法每次都是单独创建的。
new到底做了什么?
作用域中将。。。
构造函数
定义:
构造函数是一个用于创建实例,并自动设置实例的原型的函数
例子1:
function Letter(number) {
this.number = number;
}
Letter.prototype.getNumber = function() {
return this.number;
};
let a = new Letter(1);
let b = new Letter(2);
// ...
let z = new Letter(26);
console.log(
a.getNumber(), // 1
b.getNumber(), // 2
z.getNumber(), // 26
);
例子2:
function Persion(name,job){
this.name=name;
this.job=job;
}
Persion.myName=function(){
return "lsj";
}
Persion.prototype.age=26;
// Persion.age=27;
Persion.prototype.sayName=function(){
return "mahuateng";
};
console.log(new Persion("xiaoBian","manong").sayName()); //26
console.log(new Persion("xiaoBian","manong").age); //26
console.log(new Persion("xiaoBian","manong").myName()); //Uncaught TypeError