JavaScript对象的查询字符串编码

719

有没有一种快速简单的方法将JavaScript对象编码为可以通过GET请求传递的string

不使用jQuery或其他框架,只需使用纯JavaScript :)


1
如果有合适的解决方案,为什么不能使用jQuery呢? - eaglei22
5
因为当时我正在为一款IPTV机顶盒设备开发项目工作,不允许使用外部库。;-) - napolux
1
感谢回复。我经常看到这个规范,一直想知道何时会使用。现在,我有一个例子了,谢谢! :) - eaglei22
24
因为有时候你不想加载一个庞大的库来获取一个id元素。 - Aaron Butacov
我制作了一个用于将JSON转换为HTTPs查询的网站:https://kshitijdhyani.com/JSONtoHTTPSerializer/ 希望对你有所帮助。 - Kshitij Dhyani
显示剩余3条评论
49个回答

977
就像这样:

serialize = function(obj) {
  var str = [];
  for (var p in obj)
    if (obj.hasOwnProperty(p)) {
      str.push(encodeURIComponent(p) + "=" + encodeURIComponent(obj[p]));
    }
  return str.join("&");
}

console.log(serialize({
  foo: "hi there",
  bar: "100%"
}));
// foo=hi%20there&bar=100%25

这个还可以转换递归对象(使用PHP“array”符号来表示查询字符串):

serialize = function(obj, prefix) {
  var str = [],
    p;
  for (p in obj) {
    if (obj.hasOwnProperty(p)) {
      var k = prefix ? prefix + "[" + p + "]" : p,
        v = obj[p];
      str.push((v !== null && typeof v === "object") ?
        serialize(v, k) :
        encodeURIComponent(k) + "=" + encodeURIComponent(v));
    }
  }
  return str.join("&");
}

console.log(serialize({
  foo: "hi there",
  bar: {
    blah: 123,
    quux: [1, 2, 3]
  }
}));
// foo=hi%20there&bar%5Bblah%5D=123&bar%5Bquux%5D%5B0%5D=1&bar%5Bquux%5D%5B1%5D=2&bar%5Bquux%5D%5B2%5D=3


2
给定 {foo: [1,2,3], bar: "100%"},它不会出错吗? - Quentin
2
@Ofri:对于设置接收POST请求的服务器,JSON是一个不错的选择。对于GET请求,如果您向服务器发送的内容除了一些简单的参数之外还有其他内容,那么很可能您的设计是错误的。 - Tim Down
2
@Marcel 这是因为该函数没有检查 hasOwnProperty。我已经更新了你的 fiddle,现在它可以了:http://jsfiddle.net/rudiedirkx/U5Tyb/1/ - Rudie
2
“if (obj.hasOwnProperty(prop))” 这段代码必要吗? for in 语句循环遍历对象的属性,因此调用 hasOwnProperty 总是返回 true。 - Arnon
1
在 ASP.NET MVC 中,您可以使用 var k = prefix ? prefix + '.' + p : p, v = obj[p]; 以便模型绑定正常工作!谢谢。 - user1229323
显示剩余16条评论

743

29
不行,因为它不处理递归对象。 - Eddie Monge Jr
131
查询字符串本质上是键值对,设计时就不应该考虑嵌套对象的序列化。这个答案是现代的实践方式。需要点赞支持。 - Romain Durand
18
是的,它们是键值对,但并没有规定值不能是一个经过字符串编码的对象。此外,原问题要求将“JavaScript对象转换为字符串”,这个对象可能具有嵌套属性。 - Eddie Monge Jr
4
@EddieMongeJr,即使是被接受的答案(和后来的其他答案)在简要查看后也不支持嵌套对象。您可以在转换为URLSearchParams之前将嵌套对象转换为字符串。 - Mosh Feu
3
它不接受对象作为参数,只接受字符串。 - bokkie
显示剩余5条评论

252

jQuery有一个函数可以做到这一点,jQuery.param()。如果您已经在使用它,您可以使用以下代码:

示例:

var params = { width:1680, height:1050 };
var str = jQuery.param( params );

str现在包含width=1680&height=1050


162
引用Napolux(原始帖子的作者)的话:“just plain Javascript”。 :P - Sk8erPeter
6
jQuery.param()存在不良行为。尝试执行var a = []; a[2564] = 12;console.log(jQuery.param({ propertylist: a })); 看看我的意思是什么。 - akond
16
jQuery文档明确表示,您不能传递一个裸数组。 - Ariel
4
他并没有传入一个裸数组,而是传入了一个只有在索引2564处有一个值的数组。为了证明:var a = []; a[5] = 'foo'; jQuery.param({ parameters: a });结果是"parameters[]=&parameters[]=&parameters[]=&parameters[]=&parameters[]=&parameters[]=foo"。虽然这是正确的,但可能不是你所期望的。 - Chris Hall
7
问题明确要求使用原生JS。 - Bug Hunter 219
显示剩余10条评论

170

我建议使用URLSearchParams接口:

const searchParams = new URLSearchParams();
const params = {foo: "hi there", bar: "100%" };
Object.keys(params).forEach(key => searchParams.append(key, params[key]));
console.log(searchParams.toString())

或者通过将搜索对象传递到构造函数中,像这样:

const params = {foo: "hi there", bar: "100%" };
const queryString = new URLSearchParams(params).toString();
console.log(queryString);


如果您不打算支持IE(这在现在相当普遍)和某些特定的移动版本,那么这是最好的答案,因为它是纯JavaScript。 - Marcos Sandrini
6
@bmaggi无法处理嵌套属性{a: {1: 'test', 2: 'test2'}}。期望结果为:a[1]=test&a[2]=test2 - SergkeiM
@bravemaster 这是一个很棒的解决方案,特别适合Node开发者。谢谢! - Krzysztof Szala
请注意,在现代环境中,如果您的起点是一个对象,您可以使用 Object.entries。例如上述代码: const searchParams = new URLSearchParams(Object.entries(params)); - T.J. Crowder
这个支持对象嵌套吗? - Dimitri Kopriwa
请注意,在使用此方法之前,必须验证字段。例如,如果某个字段为空字符串,则查询字符串将类似于 .../?orderby=&page=1。当然,这可以在后端处理,但最好使事情更安全。 - JM217

139

使用:

Object.keys(obj).reduce(function(a,k){a.push(k+'='+encodeURIComponent(obj[k]));return a},[]).join('&')

我喜欢这个单行代码,但如果它在语义上与被接受的答案匹配,那么它可能会更受欢迎:

function serialize( obj ) {
    let str = '?' + Object.keys(obj).reduce(function(a, k){
        a.push(k + '=' + encodeURIComponent(obj[k]));
        return a;
    }, []).join('&');
    return str;
}

1
专门为reduct函数分配一行将极大地提高可读性。 - Aurelien Ribon
60
使用.map()而不是.reduce()会更简单:Object.keys(obj).map(k => k + '=' + encodeURIComponent(obj[k])).join('&') - Jannes Meyer
2
请注意,Object.keys 仅在 IE >= 9 中可用。 - Johnston
6
使用 ES6 模板字符串进一步改进了 @Jannes 的代码,代替了连接字符串的方式 - Object.keys(obj).map(k => `${k}=${encodeURIComponent(obj[k])}`).join('&') - csilk
3
如果你在编辑函数名称时使用一个比“serialize”更具体的术语,例如“encodeAsQueryString”,那么这个答案会更受欢迎。否则,每个人都必须为实际使用重命名它 - 或者更糟糕的是,不重命名它。请注意不要改变原意。 - Edward Brey
显示剩余4条评论

120

这是ES6中的一行代码:

Object.keys(obj).map(k => `${encodeURIComponent(k)}=${encodeURIComponent(obj[k])}`).join('&');

用k替换key,你就大功告成了。 - Jamie Kudla
22
警告!此方法仅适用于浅层对象。如果您有一个顶级属性是另一个对象,这个一行代码将输出 "key=%5Bobject%20Object%5D"。这只是一个提醒。 - Andrew Allbright
7
此外,这并不能分离数组。我得到的是 export?actions[]=finance,create,edit,但实际上应该是 export?actions[]=finance&actions[]=create&actions[]=edit,这是一个糟糕的标准。 - darkbluesun
7
数组几乎总是"自行解决",因为就规范而言,URL参数只是字符串,所以您需要确保您调用的服务器能够正确地读取任何不是单个字符串的内容。actions[] 是 PHP 的表示法;Django 使用多个 action 而没有 [] 后缀;一些其他的ORM/CMS需要逗号分隔的列表等等。因此,"如果不是简单字符串,请先确保您知道您的服务器需要什么"。 - Mike 'Pomax' Kamermans
我们是否真的需要在键上使用encodeURIComponent() - igorsantos07
个人而言,我喜欢过滤掉空值,因此在 map 之前添加:Object.keys(obj).filter(k => obj[k]) - Augustin Riedinger

65

使用 Node.js v6.6.3

const querystring = require('querystring')

const obj = {
  foo: 'bar',
  baz: 'tor'
}

let result = querystring.stringify(obj)
// foo=bar&baz=tor

参考文献:查询字符串


9
我认为这个评论不应该被点踩,如果这是关于服务器上的 JavaScript,那么这就是正确答案。 - Michael Benin
8
似乎不支持嵌套对象。 - Yarin Gold
@MichaelBenin 为什么你认为这只适用于Node服务器?你有检查过吗? - WEBCENTER

31

Ruby on Rails和PHP风格的查询构建器

此方法将JavaScript对象转换为URI查询字符串。它还处理嵌套的数组和对象(在Ruby on Rails和PHP语法中):

function serializeQuery(params, prefix) {
  const query = Object.keys(params).map((key) => {
    const value  = params[key];

    if (params.constructor === Array)
      key = `${prefix}[]`;
    else if (params.constructor === Object)
      key = (prefix ? `${prefix}[${key}]` : key);

    if (typeof value === 'object')
      return serializeQuery(value, key);
    else
      return `${key}=${encodeURIComponent(value)}`;
  });

  return [].concat.apply([], query).join('&');
}

使用示例:

let params = {
  a: 100,
  b: 'has spaces',
  c: [1, 2, 3],
  d: { x: 9, y: 8}
}

serializeQuery(params)
// returns 'a=100&b=has%20spaces&c[]=1&c[]=2&c[]=3&d[x]=9&d[y]=8

1
不错的例子。我在你的答案中修正了一个拼写错误。顺便说一下,如果你编辑你的 function 来排除 falsy 值(null、undefined、NaN、''),会很有趣... - developer033
1
这是一个很好的例子,通过良好的编写并结合递归和类型检查来解决这个问题。 - Noah

26

对用户187291的已接受解决方案进行了小修改:

serialize = function(obj) {
   var str = [];
   for(var p in obj){
       if (obj.hasOwnProperty(p)) {
           str.push(encodeURIComponent(p) + "=" + encodeURIComponent(obj[p]));
       }
   }
   return str.join("&");
}

在对象上检查hasOwnProperty可以使JSLintJSHint感到满意,并且如果对象不是简单字典,则可以防止意外地序列化对象的方法或其他东西。请参见JavaScript编程语言代码约定中关于for语句的段落。


20

这里有一个一行代码:

const encoded = Object.entries(obj).map(([k, v]) => `${k}=${encodeURIComponent(v)}`).join("&");

2
Object.entries在IE中不受支持。 - MBouwman
1
@MBouwman 当然,IE已经坏到无法修复的地步了,这就是为什么你必须使用babel/core-js的原因。 - chpio
@chpio 如果我没记错的话,Babel/core-js不支持Object.entries。 - MBouwman
核心支持Object.entries:https://github.com/zloirock/core-js/blob/master/README.md#ecmascript-object,即使旧的corejs2 babel运行时转换也支持它 https://github.com/babel/babel/blob/962015f7e73a4ffcec4f77fa670b47682edf53b0/packages/babel-plugin-transform-runtime/src/runtime-corejs2-definitions.js#L38 - chpio

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接