资讯专栏INFORMATION COLUMN

关于原生js的一些研究

wind3110991 / 3190人阅读

摘要:这个属性本身又是一个类型的对象,原型对象都包含一个指向构造函数的指针,而每一个实例也都包含一个指向原型对象内部的指针。

前阵子忙于准备CET-6,没时间更新文章,之后大概一个月将忙于准备期末考,也应该不会更新了,今天趁周末有时间再更新一篇最近研究的一些东西吧。

callee和caller
function inner(){
    console.log(arguments.callee);//指向拥有这个arguments对象的函数,即inner()
    console.log(arguments.callee.caller);//这个属性保存着调用当前函数的函数的引用,即outer()
    console.log(inner.caller);//[Function: outer]
}

function outer(){
    inner();
}
outer();

callee放回正在执行的函数本身的引用,它是arguments的一个属性
caller 返回一个函数的引用,这个函数调用了当前的函数。

严格模式下,不允许访问arguments.calleearguments.caller属性,主要体现在arguments.[[Get]]内部方法

严格模式下,arguments,arguments.callee,arguments.caller,arguments.callee.caller也不允许再被赋值。如下代码所示:

"use strict";
// 两次都是1
void function fn(a) {
    console.log(arguments[0]);
    a = 2;
    console.log(arguments[0]);
}(1);


//function(){}(); 会运行错误, 如下可以正确被运行.

void function(){
    console.log("hi");//hi
}();

在使用立即执行的函数表达式时,可以利用 void 运算符让 JavaScript 引擎把一个函数识别成函数表达式而不是函数声明(语句)。

实参和形参
var add = function (a,b) {
    console.log(arguments.length);//3,表示实参长度
    console.log(arguments.callee.length);//2,表示形参长度
};

add(1,2,3);


Array.prototype.slice.call(arguments)

slice有两个用法,一个是String.slice,一个是Array.slice,第一个返回的是字符串,第二个返回的是数组。

Array.prototype.slice.call(arguments)能够将arguments转成数组,那么就是arguments.toArray().slice();

因为arguments并不是真正的数组对象,只是与数组类似而已,所以它并没有slice这个方法,而Array.prototype.slice.call(arguments)可以理解成是将arguments转换成一个数组对象,让arguments具有slice()方法。 比如:

 var arr = [1,2,3,4];
 console.log(Array.prototype.slice.call(arr,2));//[3,4]

同样,还有Array.prototype.forEach.call()forEach() 方法让数组的每一项都执行一次给定的函数。

String()

我们可以用String()来确定某一变量是否是null或者undefined

var a , b = null;
String(a);//undefined
String(b);//null

直接调用String()作为方法时,将会执行类型转换,返回经过toString(value)得到的字符串字面量(与new String()不同),或者空字符串("").

window 对象
 

打开控制台,你可以看到window对象的一系列属性和方法:

new function
var a = function () {};
console.log(typeof a);//function

var b = new function () {};
console.log(typeof b);//object

var c = new Function ();
console.log(typeof c);//function

new function 是一个JavaScript中用户自定义的对象

var obj = function (name) {
    this.name = name;
};
var b = new obj("trigkit4");
console.log(b.name);  


js中的false和true
//false
console.log(Boolean(""));//false
console.log(Boolean(null));//false
console.log(Boolean(undefined));//false
console.log(Boolean(0));//false
console.log(Boolean(false));//false
console.log(Boolean(NaN));//false

//true
console.log(Boolean(" "));//true    
console.log(Boolean("NaN"));//true

除了false,null,undefined,空字符串"",数字0和NaN以外,其他所有值都被当做是真,包括true,字符串""里包含的值,以及所有对象。

valueOf() 和 toString()

valueOf()toString()方法是所有ECMAScript对象拥有的内置方法。操作对象时,valueOf()toString()会被隐式的调用。

//valueOf()
console.log(Object.valueOf());//[Function: Object]
console.log(Object.prototype.valueOf());//{}

var boo = new Boolean(1);
console.log(boo.valueOf());//true

var bar = Boolean(0);
console.log(bar.valueOf());//false

var str = String("trigkit4");
console.log(str.valueOf());//trigkit4

console.log(null.valueOf());//TypeError
console.log(undefined.valueOf());//TypeError

//toString()
console.log(Object.prototype.toString());//[object Object]
console.log(Object.toString());//function Object() { [native code] }
Object.prototype.toString.call(null);//[object Null]
Object.prototype.toString.call(undefined);//[object Undefined]
{a: "b"}.toString();//[object Object]

valueOf()方法的目的是将对象转换成最有意义的原始值([[PrimitiveValue]])。即ECMAScript的5种基本类型中的三种,布尔值、数字、字符串

valueOf方法被调用时,会调用内置的ToObject,并将this作为参数传进去。ToObject检测会根据参数类型进行数值的转换:

Undefined - 抛出TypeError异常
Null - 抛出TypeError异常
Boolean - 创建一个Boolean对象,调用ToBoolean生成[[PrimitiveValue]]
Number - 创建一个Number对象,调用ToNumber生成[[PrimitiveValue]]
String - 创建一个String对象,调用ToString生成[[PrimitiveValue]]
Object - 对象本身

ECMAScript对象的大多数操作的转换结果是字符串,这两个方法的结果是相同的。但是如果操作的对象为NumberBoolean或者Date,结果就不同了。

var foo = {
    toString: function () {
        return "foo";
    },
    valueOf: function () {
        return 5;
    }
};

console.log(foo + "bar"); // 5bar
console.log([foo, "bar"].join("")); // foobar

在这个上下文环境中,我们使用"+"操作符来使字符串连接,但是,foo并没有使用toString来转换成字符串,它使用valueOf转换成一个number,这并不是我们想要的,
但它是如何工作的,这是+运算符的算术和字符串连接超载的副作用。"+"操作符有一个明确的处理过程:

1.评估左手侧,并得到该值。
2.评估右手侧,并获得该值。
3.同时在左手和右手侧调用ToPrimitive(无提示)
4.如果任何原始值是一个字符串,然后跳到7。
5.在这两个值调用ToNumber。
6.返回值的总和。
7.在这两个值调用toString。
8.返回的值连接起来
setInterval和setTimeout
alert(1); 
setTimeout("alert(2)", 0); 
alert(3); 

执行顺序为:1,3,2,虽然延时了0ms

setTimeout 0  //正常情况下javascript都是按照顺序执行的。但是我们可能让该语句
后面的语句执行完再执行本身,这时就可以用到setTimeout延时0ms来实现了。
cookie的创建和删除

cookie可以跨越一个域名下的多个网页,但不能跨越多个域名使用。

document.cookie = “user = 值;expires = 过期时间;path = 路径访问;
domain = 域名访问;secure = 安全的https限制通信"
cookie的创建方式

设置cookie我们一般都封装成一个函数:

function addCookie(sName,sValue,day) {
    var expireDate = new Date();
    expireDate.setDate(expireDate.getDate()+day);;
//设置失效时间
    document.cookie = escape(sName) + "=" + escape(sValue) +";
    expires=" + expireDate.toGMTString();6 //escape()汉字转成unicode编码,toGMTString() 把日期对象转成字符串
} 
删除cookie

为了删除一个cookie,可以将其过期时间设定为一个过去的时间,例如:

//获取当前时间
    var date=new Date();
//将date设置为过去的时间
    date.setTime(date.getTime()-10000);
//将userId这个cookie删除
    document.cookie="userId=828; expires="+date.toGMTString();
给cookie设置终止日期

到现在为止,所有的cookie都是单会话cookie,即浏览器关闭后这些cookie将会丢失,事实上这些cookie仅仅是存储在内存中,而没有建立相应的硬盘文件。
在实际开发中,cookie常常需要长期保存,例如保存用户登录的状态。这可以用下面的选项来实现:

document.cookie="userId=828; expiress=GMT_String";

其中GMT_String是以GMT格式表示的时间字符串,这条语句就是将userId这个cookie设置为GMT_String表示的过期时间,超过这个时间,cookie将消失,不可访问。例如:如果要将cookie设置为10天后过期,可以这样实现:

 


对象和函数可以如数组一样,用属性名或方法名作为下标来访问:

对象的创建
//对象的创建
function MyFunc(){}
var obj1 = new MyFunc();//使用new操作符,借助MyFun函数,就创建了一个对象
var obj2 = new MyFunc;//函数也可以没有括号,但仍将调用该函数

可以把上面的代码改写成这种等价形式:

function MyFunc(){};
var obj1 = {};//创建一个对象
MyFunc.call(obj1);//将obj1对象作为this指针调用MyFunc函数


作用域

通过自执行的匿名函数你可以把所有原本属于全局的变量都隐藏起来:

//创建一个新的匿名函数,作为包装
(function () {
    //变量原本应该是全局的
    var msg = "Thanks for visiting";
    
    window.onunload = function () {
        console.log(msg);
    };
})();

上下文对象是通过this变量体现的,这个变量永远指向当前代码所处的对象中。

var obj = {
    yes : function(){
        //this == obj
        this.val = true;
    },
    no : function(){
        this.val = false;
    }
};
console.log(obj.val == null);//true
//执行了yes函数后,将val属性与"obj"对象关联起来
obj.yes();
console.log(obj.val == true);//true


String 原型方法的扩展
    //公共正则表达式处理函数
    String.prototype.Regular = function(reg){
        var result = true;
        if(this.length > 0){       
            if(!reg.test(this)){   
                result = false;
            }  
        } 
        return result;
    }
    
    //.trim()方法
     String.prototype.trim = function () {
        return this.replace(/(^s*)|(s*$)/g,"");
};

^表示字符串必须以后面的规则开头,而(^s*) 表示的就是以0个空格或者多个空格开头,后面的(s*$) 的意思就是, 以0个空格或者多个空格结尾。

    //判断输入内容是否为空
    String.prototype.isNull = function(){  
        return this.trim().length == 0 ? true : false; 
    } 
    
    //判断输入的字符是否为英文字母数字下划线
    String.prototype.isVersion = function(){    
        var reg = /^([a-zA-Z_])([a-zA-Z0-9_.])*$/;  
        return this.Regular(reg);
    }
    
   // 判断输入的字符串,不包括单引号
   String.prototype.isString = function(){    
        var reg = /^[^"]*$/; 
        return this.Regular(reg); 
    }
    
  //判断输入的字符是否为英文字母  
    String.prototype.isLetter = function(){   
       var reg = /^[a-zA-Z]+$/;  
       return this.Regular(reg);
   }     
constructor属性
function User(){}
var me = new User();
console.log(me.constructor);//[Function: User]

//用前一个对象的Constructor引用来创建一个新的User对象
var you = new me.constructor();
console.log(me.constructor == you.constructor);//true

Object.create
function Parent(){}
 var o = Object.create(Parent.prototype);
 console.log(o instanceof Parent);//true
 console.log(o instanceof Object);//true
 console.log(Object.prototype.toString.call(o));//[object Object]

“数据属性”是可获取且可设置值的属性。 数据属性描述符包含 value 特性,以及 writable、enumerableconfigurable 特性。 如果未指定最后三个特性,则它们默认为 false

     
function Parent(){}
var o = Object.create(Parent);
console.log(o instanceof Parent);//false
console.log(o instanceof Object);//true  

另外一个实例

var book1 = {
    title:"JS高级程序设计",
    pages : 1001,
    getTitle:function(){
        console.log(this.title);
    }
};
var book2 = Object.create(book1,{
    //title会成为所创建对象的数据属性
    title:{
        configurable:true,
        enumerable:true,
        value:"JS权威指南",
        wratable:true
    }
});
book1.getTitle();  //"JS高级程序设计"
book2.getTitle();  //"JS权威指南"

console.log(book1.hasOwnProperty("getTitle"));  //true
console.log("pages" in book2);  //true
console.log(book2.hasOwnProperty("getTitle"));  //false
console.log(book1.isPrototypeOf(book2));//true


再看另一个例子:

function Constructor(){}
obj = new Constructor();
// 上面的一句就相当于:
obj = Object.create(Constructor.prototype);
console.log(obj);//{}
console.log(Object.create(Constructor.prototype));//{}

console.log(obj instanceof Constructor);//true
console.log(Constructor.prototype.isPrototypeOf(obj));//true

var foo;
foo = {};
// 以字面量方式创建的空对象就相当于:
foo = Object.create(Object.prototype);

另外:

console.log(Object.prototype);//{}
console.log(Object.create(Object.prototype));//{}

通过Object.create(Object.prototype) 创建的实例对象就继承了Object原型下的属性和方法。

javascript所有function类型的对象都有一个prototype属性。这个prototype属性本身又是一个object类型的对象,原型对象都包含一个指向构造函数的指针,而每一个实例也都包含一个指向原型对象内部的指针。

参考:https://developer.mozilla.org...
prototype
function User(){}

var u1 = new User();
console.log(u1.prototype);//使用对象实例无法访问到prototype
console.log(User.prototype);//{},使用构造函数名访问prototype
console.log(u1.__proto__);//{},使用对象实例访问prototype的指针

//使用字面量的方式创建原型对象,这里{}就是对象
User.prototype = {
    name : "trigkit4",
    age : 22
};

使用构造函数创建原型对象和使用字面量创建对象在使用上基本相同,但还是有些区别,字面量创建的方式使用constructor属性不会指向实例,而会指向Object,构造函数创建的方式则相反

function User(){}
User.prototype = {
    name : "trigkit4",
    age : 22
};
var u1 = new User();
console.log(u1.constructor);//function Object() {[native code]}
console.log(u1 instanceof User);//true
console.log(u1.constructor == User);//false
console.log(u1.constructor == Object);//true

//如果想让字面量方式的constructor指向实例对象,可以这么做:
User.prototype = {
    constructor : User;
}

字面量方式为什么constructor会指向Object?因为User.prototype = {};这种写法其实就是创建了一个新对象:

function User(){}

User.prototype = {
    constructor : User
};
var u1 = new User();
console.log(User.constructor);//[Function: Function]
console.log(u1.constructor == User);//true

另一个例子:

 (function () {
    
        console.log(Object.prototype);//{}
        console.log(Array.prototype);//[]
        console.log(Array.prototype.push);//[Function: push]
        console.log(Function.prototype);//[Function: Empty]
        console.log(Function.prototype.bind);//[Function: bind]
    
      
    })();
Object.prototype.toString

toString()方法被调用时,会执行下面的操作步骤:

如果this的值为undefined,则返回"[object Undefined]".
如果this的值为null,则返回"[object Null]".
让O成为调用ToObject(this)的结果.
让class成为O的内部属性[[Class]]的值.
返回三个字符串"[object ", class, 以及 "]"连接后的新字符串.

由于 JavaScript 中一切都是对象,任何都不例外,对所有值类型应用Object.prototype.toString.call()

方法结果如下:

console.log(Object.prototype.toString.call(123)) //[object Number]        
console.log(Object.prototype.toString.call("123")) //[object String]      
console.log(Object.prototype.toString.call(undefined)) //[object Undefined] 
console.log(Object.prototype.toString.call(true)) //[object Boolean]      
console.log(Object.prototype.toString.call({})) //[object Object]        
console.log(Object.prototype.toString.call([])) //[object Array]      
console.log(Object.prototype.toString.call(function(){})) //[object Function]


所有类型都会得到不同的字符串,几乎完美。
JavaScript中,想要判断某个对象值属于哪种内置类型,最靠谱的做法就是通过Object.prototype.toString方法.

面向对象

下面是来自Prototype.js的一段代码:

//创建一个名为"Class"的全局对象
var Class = {
    //它只有一个函数,其作用是创建一个新的对象构造函数
    create: function(){
        //创建一个匿名的对象构造函数
        return function () {
            //调用它本身的初始化方法
            this.initialize.apply(this,arguments);
        }
    }
};

//给Object对象添加一个新的静态方法,它的作用是把属性从一个对象复制到另一个中
Object.extend = function (destination,source) {
    //遍历所有要扩展的属性
    for(property in source){
        //然后将他们添加到目标对象中
        destination[property] = source[property];
    }
};
成员操作符
function aFunc(){}//或者var aFunc = function(){};

aFunc.oProperty = "函数的一个属性";
aFunc.aMethod = function(){
    console.log("函数的一个方法");
};

console.log(aFunc["oProperty"]);//将函数当成数组以属性名作为下标来访问属性
console.log(aFunc["aMethod"]());//将函数当数组以方法名作为下标来调用方法

//遍历函数的所有属性和方法
for(var s in aFunc){
    console.log(s + "is a "+typeof(aFunc[s]));
}
特权方法与私有方法
function Constructor(msg){
    this.Message = msg;

    //私有属性
    var separator = "-";
    var owner = this;

    //私有方法
    function alertMessage(){
        console.log(owner.Message);
    }
    alertMessage();

    //特权方法(也是公有方法)
    this.aptMessage = function (str) {
        this.Message += separator + str;
        alertMessage();
    }
}

//公有方法
Constructor.prototype.clearMessage = function (str) {
    this.Message = "";
};

//静态属性
Constructor.name = "trigkit4";

//静态方法
Constructor.alertName = function (name) {
    console.log(this.name);
};

特权方法是指在构造函数的作用域中使用this关键字定义的方法;与私有方法不同,特权方法能够被公开访问,而且还能够访问私有成员。

由于私有和特权成员在函数的内部,因此它们会被带到函数的每个实例中。   
公有的原型成员是对象蓝图的一部分,适用于通过new关键字实例化的该对象的每个实例 静态成员只适用于对象的一个特殊实例

使用对象字面量语法来向prototype属性添加所有公有成员:

function Constructor(){
    //私有和特权成员
}

//公有方法
Constructor.prototype = {
    propertyA: "value1",
    propertyB: "value2",
    methodA: function(){},
    methodB: function(){}
};
删除不要的节点

DOM 元素在浏览器中所占用的空间是非常大的,要及时回收不用的节点:

var node = parentNode.removeChild(node);

node = null;//设置为空,释放空间
CollectGarbage();//IE,回收资源







文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/85743.html

相关文章

  • 关于一些前端js框架源码研究

    摘要:的作用相当于,将其转换为布尔值。用于判断一个变量是否某个对象的实例,如返回同时也会返回返回布尔值,如果为,则返回,否则返回的结果。 underscore.js源码 Underscore.js 没有对原生 JavaScript 对象进行扩展,而是通过调用 _() 方法进行封装,一旦封装完成,原生 JavaScript 对象便成为一个 Underscore 对象。 判断给定变量是否是对象 ...

    whjin 评论0 收藏0
  • 翻译 | 摆脱浏览器限制JavaScript

    摘要:在考虑宇航员的生命安全时,轻微的打嗝或者服务中断都会酿成生死事故。也许最大的挑战来自谷歌主导的简称。在最近的开发者峰会,以及今年的会议上,谷歌都为安排了大量讨论。由微软提供,是广受欢迎的编辑器,到月份已经获得了超过五百万用户。 译者:安冬 (沪江Web前端开发工程师)本文原创翻译,转载请注明作者及出处。原文地址:http://developer.telerik.com/... 技术世界...

    xfee 评论0 收藏0
  • 基于原生移动跨平台研究和实践

    摘要:基于原生主要是针对基于比较来说的,基于的我不想再讨论了,我想尝试的是从到功能都是原生,而不是用模拟的所谓原生体验。 基于原生主要是针对基于webview+h5比较来说的,基于H5的我不想再讨论了,我想尝试的是从UI到功能都是原生,而不是用H5模拟的所谓原生体验。 背景 我们从开发角度来考虑,但凡想从事长远的开发工作,都有自己的技术积累,最简单的就是一些UI组件和功能组件的封装。 举例,...

    Carl 评论0 收藏0
  • 基于原生移动跨平台研究和实践

    摘要:基于原生主要是针对基于比较来说的,基于的我不想再讨论了,我想尝试的是从到功能都是原生,而不是用模拟的所谓原生体验。 基于原生主要是针对基于webview+h5比较来说的,基于H5的我不想再讨论了,我想尝试的是从UI到功能都是原生,而不是用H5模拟的所谓原生体验。 背景 我们从开发角度来考虑,但凡想从事长远的开发工作,都有自己的技术积累,最简单的就是一些UI组件和功能组件的封装。 举例,...

    1treeS 评论0 收藏0
  • JavasScript重难点知识

    摘要:忍者级别的函数操作对于什么是匿名函数,这里就不做过多介绍了。我们需要知道的是,对于而言,匿名函数是一个很重要且具有逻辑性的特性。通常,匿名函数的使用情况是创建一个供以后使用的函数。 JS 中的递归 递归, 递归基础, 斐波那契数列, 使用递归方式深拷贝, 自定义事件添加 这一次,彻底弄懂 JavaScript 执行机制 本文的目的就是要保证你彻底弄懂javascript的执行机制,如果...

    forsigner 评论0 收藏0

发表评论

0条评论

wind3110991

|高级讲师

TA的文章

阅读更多
最新活动
阅读需要支付1元查看
<