1.包装对象
var s = "hello js"; var word = s.substring(s.indexOf(" ")+1, s.length)
JS会将字符串通过调用new String(s)的方式转换为对象,此对象有字符串的方法
属性引用结束,创建的对象销毁
2.不可变的原始值和可变对象的引用
原始值(null,undefined,布尔值,数字,字符串)是不可更改的,原始值的比较是值的比较
字符串中的方法返回新的字符串,原字符串不变;
对象的值可修改,对象的比较为引用的比较,引用同一个基对象,他们相等
3.for/in循环只遍历具有可枚举属性的值
4.函数声明语句会被提前,函数直接量只有左值会被提前(预定义)
5.构造函数动态创建编译,每次调用构造函数都会创建新的函数对象,具有顶级作用域
6.arguments指向实参对象的引用,为类数组对象,只具有数组的length属性,callee和caller 常用于递归
/*--------------------------变量和作用域--------------------------
块级作用域:{}内每段代码都具有其作用域,且变量在声明它的代码段之外是不可见的
函数作用域: 变量在声明它的函数体及函数体嵌套的任意函数体内都有定义
即函数内声明的所有变量在函数体内始终可见,声明提前(不等同于赋值提前)
JS为函数作用域,谨慎使用if,for
作用域链:一个对象列表或链表,这组对象定义了这段代码作用域中的变量
JS是基于词法作用域的语言;JS查找x的过程:从作用域链第一个对象开始查找,查找不到则继续向上查找
全局变量存在于作用域链顶端,尽量少使用全局变量*/
var scope = "global";
function f(){
console.log(scope); //输出undefined而不是"global"
var scope = "local"; //变量在此处赋值,但始终在函数体内有定义
console.log(scope); //输出local00
}
/*--------------JS模拟块级作用域:
用()将匿名函数括起并在其后加()表示立即执行*/
function test() {
(function (){
for(var i = 0;i<6;i++)
alert(i);
}) ();
alert(i); //此时i为undefined
}
/*-------------------闭包-----------------
定义:嵌套函数可以访问外部函数中定义的变量,即使在外部函数结束执行后
内部嵌套的函数继续保持他对外部函数的引用,每次调用被包裹的函数,JS会创建新的作用域并延长回收时间
*/
function getFunction(value) {
return function(value){
return value;
};
}
var a = getFunction(), b = getFunction(), c = getFunction();
console.log(a(0)); //0
console.log(a===b);//false
/*----------------函数调用与this的值
调用一个函数会暂停当前函数的执行,传递控制权和参数给新函数,函数额外接收两个附加的参数:this和arguments
调用上下文,即this的值取决于调用模式;调用模式共有四种
1.方法调用,函数为对象的一个属性 o.m():对象o成为调用上下文,即this
方法链:方法返回值是一个对象,此对象还可以再调用它的方法;当方法不需要返回值时,最好返回this
关键字this没有作用域的限制,嵌套的函数不会从调用他的函数继承this
嵌套函数作为方法调用,this指向调用他的对象,作为函数调用,this指向全局对象(非严格模式)或undefined(严格模式)
--------------------------嵌套函数this解决方法:self保存外部this
var o = {
m: function() {
var self = this; //将this保存在self中
Console.log(this===o); //t
f();
}
function f(){
console.log(this===0);//f,this为全局对象
console.log(self===0);//t,self为外部函数this值
}
}
2.函数调用,this被绑定到全局对象
/*----------------that代替this
当定义一个独立函数(不绑定于任何对象)时,this指代全局名称空间
解决方法:将包裹方法的this关键字赋值给中间变量that
obj = {};
obj.method = function() {
var that = this;
this.counter = 0;//此处this指obj
var count = function() { //独立函数,this不指代对象,指代全局空间
that.counter += 1;
console.log(that.counter);
};
count();
count();
console.log(this.counter);
};
method(); //结果为 1 2 2、
3.构造函数调用
在函数前面带上new来调用,JS暗中创建一个连接到该函数prototype成员的新对象,this会绑定到那个对象上
如果此时函数返回值不是对象,则返回this
4.间接调用,显式指定调用所需this值
调用函数f() 第一个实参为调用上下文,this,之后的实参为传入f()的值
f().call(o,1,2):使用一个不属于对象o的函数f();null,undefined被全局对象所代替,原始值被包装对象代替
f().apply(o):后面参数以数组形式写入;f().apply(o,[1,2])
对象o与方法f()不需要有任何耦合关系
*/
/*------------------面向对象----------------------------*/
1.封装
var Book = function(id, name, price){
//私有属性、方法 不能直接访问;(原理:函数作用域)
var num = 1;
function checkId() {}
//构造函数中this指向对象,特权方法,外部可访问,为对象的方法
//可访问类的私有属性和方法,new对象时会创建新的属性和方法
this.getName = function() {};
this.getPrice = function() {};
this.setName = function() {};
this.setPrice = function() {};
//对象公有属性(值类型)
this.id = id;
//对象公有属性(引用类型)
this.book = ['JavaScript', 'html'];
//对象公有方法
this.copy = function(){};
//构造器
this.setName(name);
this.setPrice(price);
};
//公有方法
//prototype添加的方法不会在new对象时创建,而是通过原型链找到
//this可以访问,因为新对象的prototype跟类的prototype指向同一对象
Book.prototype.display = function () {
// show
};
//公有方法另一种形式
Book.prototype = {
display : function(){
}
};
//类静态公有属性/方法
Book.isChinese = true;
Book.resetTime = function(){
//属于类,只能通过Book类而不能通过this,book类只能
};
//创建对象的安全模式,防止忘加new
var Book = function(title, time, type){
if (this instanceof Book) {
this.title = title;
this.time = time;
this.type = type;
}else{
return new Book(title, time, type);
}
};
---------------------继承--------------------------------
0.原型:原型对象里的所有属性方法被所有构造函数实例化出来的对象所共享,牵一发而动全身*/
//动态原型
function Person(name, age, friends, job){
this.name = name;
this.age = age;
this.friends = friends;
this.job = job;
//动态原型方法,构造函数无论执行几次,只执行一次
if (typeof this.sayName != 'function') {
Person.prototype.sayName = function(){
alert(this.name);
};
}
}
//稳妥构造函数式 durable object(稳妥对象) 环境安全要求高
//不用this,new,无公共属性
//---------------question:原型继承时构造函数如何处理
1.类式继承 核心:child.prototype = new parents()
/instanceof判断对象是否为后面类的实例
alert(child1 instanceof parents);缺点:公有属性/方法中引用类型的修改会导致此属性在全局修改; 无法向父类构造函数传递参数!!!
function SuperClass(id) {
this.id = id;
this.books = ['javascripts', 'html', 'css'];
}
SuperClass.prototype.showBook = function () {
alert(this.books);
};
function SubClass(id, name){
SuperClass.call(this, id);
this.name = name;//子类新增共有属性
}
var instance1 = new SuperClass(11);
var instance2 = new SubClass(12);
alert(instance1.id);
alert(instance2.id);
instance1.books.push('sheji');
alert(instance1.books);
alert(instance2.books);
解决了1中大部分问题,但是不会继承父类的原型方法
3.组合继承 先构造函数继承(子类定义时)后类式继承
function SuperClass(id) {this.id = id;
this.books = ['javascripts', 'html', 'css'];
}
SuperClass.prototype.showBook = function () {
alert(this.books);
};
function SubClass(id, name){
SuperClass.call(this, id);
this.name = name;
}
SubClass.prototype = new SuperClass();
instance2.showBook();
4.原型式继承
function inheritObject(o){ //o为父对象//声明过度函数对象F()
function F() {}
//过渡函数对象原型继承父对象
F.prototype = o;
//返回过度对象实例,该实例的原型继承了父对象
return new F();
}
var newBook = inheritObject(Book);
类式继承的封装,过度函数对象F()相当于其子类,同样存在类式继承的问题
5.寄生式继承,对原型继承的二次封装
var Book = {name : 'js',
alikeBook : 'css'
};
function createBook(Book){
//通过原型继承方式创建新对象
var o = new inheritObject(Book);
//拓展新对象
o.getName = function(){
alert(name);
};
return o;
}
6.寄生组合式继承
function inheritPrototype(subClass, superClass){//复制一份父类原型副本保存在变量中
var p = inheritObject(superClass.prototype);
//修正因为重写子类原型导致的子类constructor属性修改
p.constructor = superClass;
//设置子类的原型
subClass.prototype = p;
}
7.模拟Ext.js继承
function extend(sub, sup){//目的:只继承函数原型对象
var F = new Function(); //1.空函数F进行中转
F.prototype = sup.prototype;//2.空函数原型对象和超类圆形对象转换
sub.prototype = new F(); //3.原型继承
sub.prototype.constructor = sub;//4.还原子类构造器
//保存父类原型对象,方便解耦,
sub.superClass = sup.prototype;//子类静态属性接收父类原型对象
//判断父类原型对象构造器(加保险)
if (sup.prototype.constructor == Object.prototype.constructor) {
sup.prototype.constructor = sup;
}
}
//注意:1.在子类原型上进行修改后继承修改不起作用,会在继承时被冲掉
// 2.先继承后对子类原型进行修改,拓展子类方法
//修改父类原型-------作用未知
/*--------------------------接口的鸭式辨型法------------------------------------*/
/*接口类 class interface
参数1:接口的名字name
参数2:methods接收方法名称的array类型的集合
*/
var interface = function(name, methods){
//判断参数个数
if (arguments.length != 2) {
throw new Error('2 arguments are must');
}
this.name = name;
this.methods = [];//接收参数methods的方法名
for(var i=0,len=methods.length; i<len; i++){
if (typeof methods[i] !== 'string') {
throw new Error('interface method name');
}
this.methods.push(methods[i]);
}
};
/*.具体的实现类*/
//1.实例化接口对象
var compositerInterface = new interface('compositerInterface', ['add', 'remove']);
var FormitemInterface = new interface('FormitemInterface', ['update', 'selete']);
//2.具体的实现类
compositerImp1 = function(){
};
//3.实现接口方法
compositerImp1.prototype.add = function(obj){
alert('add');
};
compositerImp1.prototype.remove = function(obj){
alert('remove');
};
compositerImp1.prototype.update = function(obj){
alert('update');
};
compositerImp1.prototype.selete = function(obj){
alert('selete');
};
/*检验接口的方法*/
interface.ensureImp = function(object){
if (arguments.length<2) {//参数小于2意味着类没有需要实现的接口
throw new Error('interface number error');
}
//获得接口实例对象
for (var i = 1; i < arguments.length; i++) {
var instanceInterface = arguments[i];
//判断参数是否是接口类的类型
if (instanceInterface.constructor != interface) {
throw new Error('arguments constructor is not interface');
}
//循环接口实例对象的每个方法
//instanceInterface有接收参数的数组methods属性
for (var j = 0; j < instanceInterface.methods.length; i++) {
var methodName = instanceInterface.methods[j];//只是方法名,string
if (!object[methodName] || typeof object[methodName] != 'function') {
throw new Error('the method is not found');
}
}
}
};
var c1 = new compositerImp1();
c1.add();
interface.ensureImp(c1, compositerInterface, FormitemInterface);